Программы
Книги
Статьи

27_img

А. В. Белов Создаем устройства на микроконтроллерах

Данная книга представляет собой практическое пособие по разработке электронных схем с применением микроконтроллеров и управляющих программ к ним. Основа книги - это ряд практических примеров, которые от простого к сложному раскрывают принципы построения схем и написания программ для микроконтроллеров. Специально разработанные примеры вводят читателя в мир программирования с самых азов, пройти по всем этапам усложнения задачи и заканчиваются описанием нескольких интересных конструкций имеющих определенную практическую ценность.После урока по программированию и схемотехники читатель получает подробные сведения о том, как происходит написание трансляция и отладка программ, познакомится с программными средствами, оЬлегчающими редактирование и отладку программ. В заключении вы познакомитесь с принципами построения программаторов для прошивки оттранслированных программ в микросхему микроконтроллера.


Скачать книгу  

Описание

А. В. Белов
Создаем устройства
на микроконтроллерах

Наука и Техника
Санкт-Петербург
2007


Белов А. В.
Создаем устройства на микроконтроллерах. — СПб.: Наука и Техника,
2007. — 304 е.: ил.
ISBN 978-5-94387-364-3
Серия «Радиолюбитель»
Данная книга представляет собой практическое пособие по разработке
электронных схем с применением микроконтроллеров и управляющих программ
к ним. Основа книги - это ряд практических примеров, которые от простого к
сложному раскрывают принципы построения схем и написания программ для
микроконтроллеров. Специально разработанные примеры вводят читателя в
мир программирования с самых азов, пройти по всем этапам усложнения задачи
и заканчиваются описанием нескольких интересных конструкций имеющих
определенную практическую ценность.
После урока по программированию и схемотехники читатель получает подробные
сведения о том, как происходит написание трансляция и отладка программ,
познакомится с программными средствами, оЬлегчающими редактирование и
отладку программ. В заключении вы познакомитесь с принципами построения
программаторов для прошивки оттранслированных программ в микросхему
микроконтроллера, рассмотрите конкретную схему программатора и научитесь
работать с программой, управляющей этим программатором. Книга рассчитана
на широкий круг читателей. Она будет полезна разработчикам электронных
устройств, радиолюбителям и студентам технических ВУЗов.


Содержание
Вступление 10
Глава 1. Написание программ для микроконтроллеров AVR ... 12
1.1. Общие положения 13
1.2. Простейшая программа 16
Постановка задачи 16
Принципиальная электрическ?я схема 16
Алгоритм 20
Программа на Ассемблере 21
Директивы 23
Операторы 26
Описание программы (листинг 1.1) 29
Программа на языке си 32
Работа программы, написанной на языке Си 39
Описание программы (листинг 1.2) 47
1.3. Переключающийся светодиод 49
Постановка задачи 49
Принципиальная схема 49
Алгоритм 49
Программа на Ассемблере 54
Описание программы (листинг 1.3) 56
Программа на языке си 57
Описание программы (листинг 1.4) 59
1.4. Боремся с дребезгом контактов 60
Постановка задачи 60
Схема 60
Алгоритм 60
Программа на Ассемблере 62


Описание программы (листинг 1.5) 65
Программа на языке си 67
1.5. Мигающий светодиод 70
Постановка задачи 70
Схема 70
Алгоритм программы 70
Программа на Ассемблере 71
Описание программы (листинг 1.7) 72
Программа на языке си 74
1.6. Бегущие огни 76
Постановка задачи 76
Схема 76
Алгоритм 77
Выполнение алгоритма сдвига 78
Программа на Ассемблере 78
Описание программы (листинг 1.9) 82
Программа на языке си 84
1.7. Использование таймера 88
Постановка задачи 88
Схема 88
Алгоритм 88
Программа на Ассемблере 90
Описание программы (листинг 1.11) 92
Программа на языке си 95
1.8. Использование прерываний по таймеру 98
Постановка задачи 98
Схема 98
Алгоритм 98
Программа на Ассемблере 100
Описание программы (листинг 1.13) 104
Программа на языке си 110
Описание программы (листинг 1.14) 114


1.9. Формирование звука 117
Постановка задачи 117
Схема 117
Алгоритм 119
Программа на Ассемблере 120
Описание программы (листинг 1.15) 125
Программа на языке си 130
Описание программы (листинг 1.16) 135
1.10. Музыкальная шкатулка 137
Постановка задачи 137
Схема., 137
Алгоритм 138
Кодируем мелодии 140
Алгоритм работы музыкальной шкатулки 141
Программа на Ассемблере 142
Описание программы (листинг 1.17) 149
Процедура вычисления адреса 150
Текст программы «шаг за шагом» 1.51
Особенности программы 151
Подрограмма формирования задержки 155
Программа на языке си 157
Описание программы (листинг 1.18) 161
1.11. Кодовый замок 165
Постановка задачи 165
Алгоритм 166
Схема 169
Программа на Ассемблере 170
Описание программы (листинг 1.19) 177
Процедура записи ключевой комбинации В EEPROM 189
Процедура проверки кода 190
Процедура открывания замка 191
Программа на языке си 192
Описание программы (листинг 1.20) 194


1.12. Кодовый замок с музыкальным ЗВОНКОМ 207
Постановка задачи 207
Алгоритм 207
Схема 208
Программа на Ассемблере 209
Программа на языке си 221
Глава 2 . Отладка и трансляция программ 227
2.1. Программная среда AVR Studio 228
2.1.1. Общие сведения 228
Отладка программы 228
Программный отладчик 229
Аппаратный отладчик 229
Полнофункциональные программные имитаторы электронных устройств 230
Внутренний отладчик микроконтроллеров AVR 231
Программная среда «AVR Studio» 232
2.1.2. Описание интерфейса 235
Главная панель программы «AVR Studio» 235
2.1.3. Создание проекта 242
2.1.4. Трансляция программы 245
Форматы файлов 245
Формат НЕХ-файл а 245
Процедура трансляции 246
2.1.5. Отладка программы 248
Ошибки алгоритма и его реализации 248
Этапы процесса отладки 249
Применение точек останова 252
Просмотр и изменение содержимого введенных переменных 255
2.1.6. Исправление ошибок 256
2.1.7. Создание проектов на языке си 257
2.2. Система программирования Code Vision AVR 259
2.2.1. Общие сведения 259
2.2.2. Интерфейс системы Code Vision AVR 261


Окно номер 1 261
Окно номер 2 263
Окно номер 3 263
Создание проекта без использования мастера 263
Отладка программы 267
2.3. Программаторы 269
2.3.1. Общие сведения 269
2.3.2. Схема программатора 271
Универсальные и специализированные программаторы 271
Способ подключения программатора к компьютеру 271
Внутрисхемное программирование 274
Питание программатора 274
2.3.3. Программа управления программатором 275
Знакомство с программой PonyProg 275
Алгоритм действий 276
Программирование микросхем 278
Режимы работы программатора 285
ПРИЛОЖЕНИЕ 287
Сводная таблица команд Ассемблера микроконтроллеров AVR
Группа команд логических операций
Группа команд арифметических операций
Группа команд операций с разрядами
Группа команд сравнения
Группа команд операций сдвига
Группа команд пересылки данных
Группа команд управления системой
Группа команд передачи управления (безусловная г1ередача управления)
Группа команд передачи управления (пропуск команды по условию)
Группа команд передачи управления (передача управления по условию)
Список литературы 295
Список ССЫЛОК в Интернет 295


Для заметок


Эту книгу посвящаю моей маме,
Беловой Вере Павловне
Вступление
Современную микроэлектронику трудно представить без такой
важной составляющей, как микроконтроллеры. Микроконтроллеры
незаметно завоевал весь мир. В последнее время на помощь человеку
пришла целая армия электронных помощников. Мы привыкли к ним
и часто даже не подозреваем, что во многих таких устройствах
работает микроконтроллер.
Микроконтроллерные технологии очень эффективны. Одно и то же
устройство, которое раньше собиралось на традиционных элемен-
тах, будучи собрано с применением микроконтроллеров, становится
проще. Оно не требует регулировки и меньше по размерам.
Кроме того, с применением микроконтроллеров появляются
практически безграничные возможности по добавлению новых
потребительских функций и возможностей к уже существующим
устройствам. Достаточно просто поменять программу!
Где же применяются микроконтроллеры? Да просто везде!
Посмотрите вокруг себя. У вас в квартире стоит современный
телевизор? Не сомневайтесь: в нем есть, как минимум, один мик-
роконтроллер. У вас есть на руке электронные часы? Современные
часы — это просто специализированный микроконтроллер.
Ну, а мобильные телефоны — это, вообще, миниатюрные ком-
пьютеры! Возможно, у вас есть игровая приставка, карманная
электронная игра, современная микроволновая печь, стиральная
машина, проигрыватель лазерных дисков, калькулятор. Во всех
этих устройствах работает микроконтроллер. Микроконтроллер
применяется и в бытовых приборах, и в сложных промышленных
установках.
Однако задача разработки радиоэлектронных устройств с примене-
нием микроконтроллеров требует знания и понимания принципов
их работы, но главное — умение составлять управляющие програм-
мы. Без программы микроконтроллер просто кусочек пластмассы с
ножками. В данной книге мы научимся разрабатывать микрокон-
троллерные устройства.


Мы будем ставить перед собой задачи, начиная от самых простых,
имеющих ценность только лишь как элементарные примеры.
Постепенно будем задачи усложнять, дойдем до вполне реальных
практических разработок.
В качестве базовой микросхемы для всех приведенных в книге
примеров использована микросхема ATiny2313 популярной мик-
ропроцессорной серии AVR фирмы Atmel. Предполагается, что
читатель имеет основные представления, как о принципах построе-
ния микропроцессорной техники, так и об основных особенностях
архитектуры микроконтроллеров семейства AVR.
Если ВЫ не обладаете подобными знаниями, рекомендую обра-
титься К новому изданию Самоучителя по микропроцессорной
технике [3], где все это описывается достаточно подробно. Полное
и подробное описание микросхемы ATiny2313 вы можете найти в
моей новой книге Микроконтроллеры AVR в радиолюбительской
практике [4].
Рассмотрим теперь книгу, лежащую перед вами. Первая глава пред-
ставляет собой ряд примеров, при помощи которых шаг за шагом
раскрываются основные секреты программирования. Вы начнете
с простейшей программы и закончите такими устройствами, как
музыкальная шкатулка и кодовый дверной замок. Специально раз-
работанные примеры построены таким образом, что представляют
собой ряд уроков программирования, позволяющих изучить язык
программирования от практически нулевого уровня, до уровня,
позволяющего писать программы средней сложности.
Каждый новый пример начинается с постановки задачи. Затем вы
можете увидеть процесс построения алгоритма. Далее мы вместе
создадим электрическую схему, и, наконец, увидим, как создается
управляющая программа для этой схемы.
Все программные примеры приведены в двух вариантах: на языке
Ассемблера и на языке СИ. Каждый пример снабжен подробным
описанием. И это не просто сухое описание программ.
Вы получите ряд уроков, из которых узнаете:
? основные приемы программирования на Ассемблере и языке СИ;
? правила построения программ.
Вы сможете сравнить два этих языка, оценить основные преиму-
щества и недостатки каждого из них.
Во второй главе книги мы научимся работать в программной среде
AVR Studio и среде Code Vision. Если выражаться простым языком,
10


то каждая из указанных выше программных сред представляет собой
компьютерную программу, специально предназначенную для напи-
сания и отладки программ для микроконтроллеров фирмы AVR.
Программа AVR Studio разработана фирмой Atmel и позволяет
создавать, транслировать и отлаживать программы на Ассемблере.
Программа Code Vision позволяет создавать и отлаживать програм-
мы на языке СИ.
В конце второй главы книги вы познакомитесь с методами прошив-
ки программ В программную память микросхемы микроконтрол-
лера. Будет подробно описан популярный программатор PonyProg,
его схема и управляющая программа.
Автор надеется, что настоящая книга будут полезна широкому
кругу начинающих конструкторов электронной техники, радио-
любителей и студентов технических вузов, и будет благодарен за
любые замечания и комментарии по книге. Все замечания прошу
высылать по адресу Украина, г. Симферополь, ул. Русская, 194 или
по E-mail: avbelov@by.ru.
Дополнительную информацию об этой, а также о других моих кни-
гах ВЫ можете почерпнуть на специальном сайте поддержки моих
книг по адресу: http://book.microprocessor.by.ru/
Ознакомиться с новинками и приобрести книги из любой страны
мира можно через официальный сайт и Интернет-магазин изда-
тельства Наука и Техника www.nit.com.ru.
Удачи Вам во всех делах!
11


12


ГЛАВ
А 1
На
пи
са
ни
е
пр
ог
ра
мм
дл
я
ми
кр
ок
он
тр
ол
ле
ро
в
AV
R
5 этой
главе
мы
научи
мся
создав
ать
свои
собств
енные
микро
процес
сорные
устро
йства
и
писат
ь для
них
програ
ммы.
Данна
я
глава
предс
тавля
ет
собой
практ
ически
й курс
по
конст
руиров
анию
прост
ейших
устро
йств
на
микро
контр
оллера
х.
Прочи
тав
эту
главу
вы
научи
тесь
выпол
нять
поста
новку
задачи
,
соста
влять
алгори
тм
работ
ы
микро
процес
соного
устро
йства,
разраб
атыва
ть
элект
рическ
ую
схему
для
конкре
тной
задачи
,
создав
ать
програ
мму на
языке
Ассем
блера,
создав
ать
програ
мму на
языке
СИ.


1.1. Общие положения
Главная задача этой книги — научиться создавать программы для
микроконтроллеров. Как можно узнать из [3] программа для мик-
роконтроллера — это набор кодов, который записывается в его
специальную программную память. Программу должен написать
программист, который разрабатывает ту или иную конкретную мик-
ропроцессорную систему.
Однако программист никогда не имеет дело с кодами. Часто про-
граммист даже и не задумывается о том, какой код соответствует той
или иной команде. Дело в том, что для человека программирование
В кодах очень неудобно. Человек же не компьютер.
Для человека удобнее оперировать с командами, каждой из которых
имеет свое осмысленное название. Поэтому для написания программ
человек использует языки программирования.
Определение. Язык программирования — это специально разрабо-
танный язык, служащий посредником между машиной
и человеком. Как и обычный человеческий язык, любой
язык программирования имеет свой словарь (набор
слов) и правила их написания.
В качестве слов в языке программирования выступают:
? команды (операторы);
? специальные управляющие слова;
? названия регистров;
? числовые выражения.
Главная задача языка — однозначно описать последовательность
действий, которую должен выполнить ваш микроконтроллер. В то
же время язык должен быть удобен и понятен человеку.
В процессе создания программы программист просто пишет ее текст
на компьютере точно так же, как он пишет любой другой текст. Затем
программист запускает специальную программу — транслятор.
Определение. Транслятор — это специальная программа, кото-
рая переводит текст, написанный программистом,
в машинные коды, то есть в форму, понятную для
микроконтроллера.
13


Написанный программистом текст программы называется исходным
или объектным кодом. Код, полученный в результате трансляции,
называется результирующим или машинным кодом. Именно этот
код записывается в программную память микроконтроллера. Для
записи результирующего кода в программную память применяются
специальные устройства — программаторы. О программаторах мы
подробно поговорим В последней главе этой книги.
Все языки программирования делятся на две группы:
? ЯЗЫКИ низкого уровня (машиноориентированные);
? языки высокого уровня.
Типичным примером машиноориентированного языка программирова-
ния является ЯЗЫК Ассемблер. Этот язык максимально гфиближен к сис-
теме команд микроконтроллера. Каждый оператор этого языка —это,
по сути, словесное название какой-либо конкретной команды.
В процессе трансляции такая команда просто заменяется кодом
операции. Составляя программу на языке Ассемблер, программист
должен оперировать теми же видами данных, что и сам процессор,
ТО есть байтами и битами.
Специфика языка Ассемблер состоит еще и в том, что набор операторов
для этого языка напрямую зависит от системы команд конкретного микро-
контроллера. Поэтому, если два микроконтроллера имеют разную систему
команд, то и ЯЗЫК Ассемблер для каждого такого микроконтроллера будет
свой. В данной книге мы будем изучать одну конкретную версию языка
Ассемблер. А именно Ассемблер для микроконтроллеров AVR.
В недавнем прошлом язык Ассемблер был единственным языком про-
граммирования для микроконтроллеров. Только он позволял эффектив-
но использовать скудные ресурсы самых первых микросхем. Однако в
настоящее время, когда возможности современных микроконтроллеров
значительно возросли, для составления программ все чаще использу-
ются ЯЗЫКИ высокого уровня, такие как Бейсик, СИ и т. п.
Эти языки В свое время были разработаны для больших настоящих ком-
пьютеров. Но сейчас широко используются также и для микроконтрол-
леров. Языки высокого уровня отличаются тем, что они гораздо больше
ориентированы на человека. Большинство команд языков высокого
уровня не связаны с конкретными командами микроконтроллера.
Такие ЯЗЫКИ оперируют уже не с байтами, а с привычными нам деся-
тичными числами, а также с переменными, константами и другими
элементами, знакомыми нам из математики. Константы и переменные
могут принимать привычные для нас значения.
14


Например, положительные, отрицательные значения, вещественные
значения (десятичные дроби) и т. п. Со всеми переменными и кон-
стантами можно выполнять знакомые нам арифметические операции
и даже алгебраические функции.
Транслятор с языка высокого уровня производит более сложные пре-
образования, чем транслятор с Ассемблера. Но в результате тоже полу-
чается программа в машинных кодах. При этом транслятор использует
все ресурсы микроконтроллера по своему усмотрению. В каких именно
регистрах или ячейках памяти она будет хранить значения описанных
вами переменных, по каким алгоритмам она будет вычислять матема-
тические функции, программист обычно не задумывается.
Программа-транслятор выбирает все это сама. Поэтому задача эффек-
тивности алгоритма полученной в результате трансляции программы
целиком ложится на программу-транслятор. В целом, программы,
написанные на языках высокого уровня, занимают в памяти микро-
контроллера объем на 30—40 % больший, чем аналогичные програм-
мы, написанные на языке Ассемблер.
Однако если микроконтроллер имеет достаточно памяти и запас
по быстродействию, то это увеличение программы — не проблема.
Преимуществом же языков высокого уровня является существенное
ускорение процесса разработки программы. Из всех языков высокого
уровня самым эффективным, пожалуй, является язык СИ. Поэтому
для иллюстрации языков высокого уровня мы выберем именно его.
Изучение приемов программирования мы будем осуществлять на
ряде конкретных примеров:
? каждый пример будет начинаться с постановки задачи;
? затем мы научимся выбирать схемное решение;
? лишь после этого будут представлены примеры программ.
Для каждой задачи в книге приводятся два варианта программы. Одна
на языке Ассемблер, вторая на языке СИ. В результате вы сможете не
только научиться азам программирования на двух языках, но и понять
все достоинства и недостатки каждого из языков программирования.
Все примеры, приведенные в моей книге, вы можете попробовать
вживую на вашем компьютере. Причем текст программ не обязатель-
но набирать вручную. Все приведенные в книге примеры вы можете
скачать из Интернета с сайта http://book.microprocessor.by.ru. Кроме
точной копии всех программ, приведенных в данной книге, на сайте
ВЫ найдете целый ряд дополнительных примеров. Все подробности
смотрите на самом сайте.
15


1.2. Простейшая программа
Постановка задачи
Самая простая задача, которую можно придумать для микроконтрол-
лера, может звучать следующим образом:
«Разработать устройство управления одним светодиодным индика-
тором при помощи одной кнопки. При нажатии кнопки светодиод
должен зажечься, при отпускании — погаснуть».
С практической точки зрения это совершенно бессмысленная задача,
так как для ее решения проще обойтись без микропроцессора. Но в
качестве примера для обучения подойдет прекрасно.
Принципиальная электрическая схема
Попробуем разработать принципиальную электрическую схему, спо-
собную выполнять описанную выше задачу. Итак, к микроконтроллеру
нам нужно подключить светодиод и кнопку управления. Как мы уже
говорили, для подключения к микроконтроллеру AVR любых внешних
устройств используются порты ввода—вывода. Причем каждый такой
порт способен работать либо на ввод, либо и на вывод.
Удобнее всего светодиод подключить к одному из портов, а кноп-
ку — К другому. В этом случае управляющая программа должна
будет настроить порт, к которому подключен светодиод, на вывод, а
порт, К которому подключена кнопка, на ввод. Других специальных
требований к микроконтроллеру не имеется. Поэтому выберем мик-
роконтроллер.
Очевидно, что нам нужен микроконтроллер, который имеет не менее
двух портов. Данным условиям удовлетворяют многие микрокон-
троллеры AVR. Я предлагаю остановить свой выбор на довольно
интересной микросхеме ATiny2313. Эта микросхема, хотя и относится
К семейству «Tiny», на самом деле занимает некое промежуточное
место между семейством «Tiny» и семейством «Mega». Она не так
перегружена внутренней периферией и не столь сложна, как мик-
росхемы семейства «Mega». Но и не настолько примитивна, как все
остальные контроллеры семейства «Tiny».
16


Эта микросхема содержит два основных и один дополнительный
порт ввода—вывода, имеет не только восьмиразрядный, но и шест-
надцатиразрядный таймер/счетчик. Имеет оптимальные размеры
(20-выводной корпус). И, по моему мнению, идеально подходит в
качестве примера для изучения основ программирования. К тому же
эта микросхема имеет и еще одну привлекательную особенность. По
набору портов и расположению выводов она максимально прибли-
жена К микроконтроллеру АТ89С2051, который был использован в
качестве примера в первом издании настоящей книги.
Итак, если не считать порта А, который включается только в особом
режиме, который мы пока рассматривать не будем, микроконтрол-
лер имеет два основных порта ввода—вывода (порт В и порт D).
Договоримся, что для управления светодиодом мы будем использовать
младший разряд порта В (линия РВ.О), а для считывания информации с
КНОПКИ управления используем младший разряд порта D (линия PD.O).
Полная схема устройства, позволяющего решить поставленную выше
задачу, приведена на рис. 1.1.
Для подключения кнопки S1 использована классическая схема.
В исходном состоянии контакты кнопки разомкнуты. Через резистор
R1 на вход PD.O микроконтроллера подается «плюс» напряжения
питания, что соответствует сигналу логической единицы.
При замыкании кнопки напряжение падает до нуля, что соответствует
логическому нулю. Таким образом, считывая значение сигнала на со-
ответствующем выводе порта, программа может определять момент
нажатия кнопки. Несмотря на простоту данной схемы, микроконтрол-
лер AVR позволяет ее упростить. А именно, предлагаю исключить
резистор R1, заменив его внутренним нагрузочным резистором мик-
роконтроллера. Как уже говорилось выше, микроконтроллеры серии
AVR имеют встроенные нагрузочные резисторы для каждого разряда
порта. Главное при написании программы — не забыть включить
программным путем соответствующий резистор.
Подключение светодиода также выполнено по классической схеме.
Это непосредственное подключение к выходу порта. Каждый выход
микроконтроллера рассчитан на непосредственное управление све-
тодиодом среднего размера с током потребления до 20 мА. В цепь
светодиода включен токоограничивающий резистор R3.
Для того, чтобы зажечь светодиод, микроконтроллер должен подать
на вывод РВ.О сигнал логического нуля. В этом случае напряжение,
приложенное к цепочке R2, VD1, окажется равным напряжению
питания, что вызовет ток через светодиод, и он загорится. Если же
17


на вывод PD.0 подать сигнал логической единицы, падение напряже-
ния на светодиоде и резисторе окажется равным нулю, и светодиод
погаснет.
Кроме цепи подключения кнопки и цепи управления светодиодом,
на схеме вы можете видеть еще несколько цепей. Это стандартные
цепи, обеспечивающие нормальную работу микроконтроллера.
Кварцевый резонатор Q1 обеспечивает работу встроенного такто-
вого генератора. Конденсаторы С2 и СЗ — это цепи согласования
кварцевого резонатора.
Элементы С1, R2 — это стандартная цепь начального сброса. Такая
цепь обеспечивает сброс микроконтроллера в момент включения
питания. Еще недавно подобная цепь была обязательным атрибутом
любой микропроцессорной системы. Однако технология производства
микроконтроллеров достигла такого уровня, что обе эти цепи (внеш-
ний кварц и цепь начального сброса) теперь можно исключить.
18


Большинство микроконтроллеров AVR, кроме тактового генератора с
внешним кварцевым резонатором, содержат внутренний RC-генера-
тор, не требующий никаких внешних цепей. Если вы не предъявляете
высоких требований к точности и стабильности частоты задающего
генератора, то микросхему можно перевести в режим внутреннего
RC-генератора и отказаться как от внешнего кварца (Q1), так и от
согласующих конденсаторов (С2 и СЗ).
Цепь начального сброса тоже можно исключить. Любой микрокон-
троллер AVR имеет внутреннюю систему сброса, которая в боль-
шинстве случаев прекрасно обеспечивает стабильный сброс при
включении питания. Внешние цепи сброса применяются только при
наличии особых требований к длительности импульса сброса. А это
бывает лишь в тех случаях, когда микроконтроллер работает в усло-
виях больших помех и нестабильного питания.
Все описанные выше переключения производятся при помощи соот-
ветствующих fuse-переключателей. Как это можно сделать, мы увидим
В следующей главе. Три освободившихся вывода микроконтроллера
могут быть использованы как дополнительный порт (порт А). Но в
данном случае в этом нет необходимости.


Упростим схему, показанную на рис. 1.1, с учетом описанных выше
возможностей. От внешнего кварца пока отказываться не будем. Он
нам пригодиться чуть позже, когда мы начнем формировать времен-
ные интервалы. Доработанная схема изображена на рис. 1.2.
Алгоритм
Итак, схема у нас есть. Теперь нужно приступать к разработке про-
граммы. Разработка любой программы начинается с разработки
алгоритма.
Определение. Алгоритм — это последовательность действий,
которую должен произвести наш микроконтроллер^
чтобы достичь требуемого результата. Для простых
задач алгоритм можно просто описать словами. Для
более сложных задач алгоритм рисуется в графическом
виде.
В нашем случае алгоритм таков: После операций начальной настройки
портов микроконтроллер должен войти в непрерывный цикл, в про-
цессе которого он должен опрашивать вход, подключенный к нашей
кнопке, и В зависимости от ее состояния управлять светодиодом.
Опишем это подробнее.
Операции начальной настройки:
? установить начальное значение для вершины стека микроконт-
роллера;
? настроить порт В на вывод информации;
? подать на выход РВ.О сигнал логической единицы (потушить
светодиод);
? сконфигурировать порт D на ввод;

? включить внутренние нагрузочные резисторы порта D.
Операции, составляющее тело цикла:
? прочитать состояние младш'его разряда порта PD (PD.O);
20


если значение этого разряда равно единице, выключить светодиод;
если значение разряда PD.0 равно нулю, включить светодиод;
перейти на начало цикла.
Программа на Ассемблере
Для создания программ мы используем версию Ассемблера, предло-
женную разработчиком микроконтроллеров AVR — фирмой Atmel.
А также воспользуемся программным комплексом «AVR Studio»,
разработанным той же фирмой и предназначенным для создания,
редактирования, трансляции и отладки программ для AVR на
Ассемблере. Подробнее о программе «AVR Studio» мы поговорим в
последней главе книги.
А сейчас наша задача — научиться создавать программы. Изучение
языка будет происходить следующим образом. Я буду приводить
готовый текст программы для каждой конкретной задачи, а затем
подробно описывать все его элементы и объяснять, как программа
работает.
Текст возможного варианта программы, реализующий поставленную
выше задачу, приведен в листинге 1.1. Прежде, чем мы приступим к
описанию данного примера, я хотел бы дать несколько общих понятий
о языке Ассемблер.
Программа на Ассемблере представляет собой набор команд и
комментариев (иногда команды называют инструкциями). Каждая
команда занимает одну отдельную строку. Их допускается переме-
жать пустыми строками. Команда обязательно содержит оператор,
который выглядит как имя выполняемой операции.
Некоторые команды состоят только из одного оператора. Другие
же команды имеют один или два операнда (параметра). Операнды
записываются в той же строке сразу после оператора, через пробел.
Если операндов два, их записывают через запятую. Так, в строке 6
нашей программы записана команда загрузки константы в регистр
общего назначения. Она состоит из оператора Idi и двух операндов
tempnRAMEND.
В случае необходимости перед командой допускается ставить так
называемую метку. Она состоит из имени метки, заканчивающимся
двоеточием. Метка служит для именования данной строки программы.
21


Затем это имя используется в различных командах для обращения к
помеченной строке.
При выборе имени метки необходимо соблюдать следующие правила:
? имя должно состоять из одного слова, содержащего только
латинские буквы и цифры;
? допускается также применять символ подчеркивания;
? первым символом метки обязательно должна быть буква или
символ подчеркивания.
Строка 16 нашей программы содержит метку с именем main. Метка
не обязательно должна стоять в строке с оператором. Допускается
ставить метку в любой строке программы. Кроме команд и меток,
программа содержит комментарии.
Определние. Комментарий — это специальная запись в теле про-
граммы, предназначенная для человека. Компьютер в
процессе трансляции программы игнорирует все ком-
ментарии. Комментарий может занимать отдельную
строку, а может стоять в той же строке, что и ко-
манда. Начинается комментарий с символа «точка с
запятой». Все, что находится после точки с запятой
до конца текущей строки программы, считается ком-
ментарием.
Если В уже готовой программе вы поставите точку с запятой в начале
строки перед какой-либо командой, то данная строка для транслятора
как бы исчезнет. С этого момента транслятор будет считать всю эту
строку комментарием. Таким образом, можно временно отключать
отдельные строки программы в процессе отладки (то есть при поиске
ошибок В программе).
Кроме операторов, в языке Ассемблер применяются псевдооперато-
ры или директивы. Если оператор — это некий эквивалент реальной
команды микроконтроллера и в процессе трансляции заменяется
соответствующим машинным кодом, который помещается в файл
результата трансляции, то директива, хотя по форме и напоминает
оператор, но не является командой процессора.
Определние. Директивы — это специальные вспомогательные коман-
ды для транслятора, определяющие режимы трансляции
и реализующие различные вспомогательные функции.
22


Далее из конкретных примеров вы поймете, о чем идет речь. В данной
конкретной версии Ассемблера директивы выделяются особым обра-
зом. Имя каждой директивы начинается с точки. Смотри листинг 1.1,
строки с 1 по 5.
При написании программ на Ассемблере принято соблюдать особую
форму записи:
? программа записывается в несколько колонок (см. листинг
1.1);
? аналогичные элементы разных команд принято размещать друг
под другом;
? самая первая (левая) колонка зарезервирована для меток;
? если метка отсутствует, место в колонке пустует.
? следующая колонка предназначена для записи операторов
? затем идет колонка для операндов.
? оставшееся пространство (крайняя колонка справа) предна-
значено для комментариев.
В некоторых случаях, например когда текст команды очень длинный,
допускается нарушать этот порядок. Но по возможности нужно
оформлять программу именно так. Оформленная подобным обра-
зом программа более наглядна и гораздо лучше читается. Поэтому
привыкайте писать программы правильно.
Итак, мы рассмотрели общие принципы построения программы
на Ассемблере. Теперь пора приступать к подробному описанию
конкретной программы, приведенной в листинге 1.1. И начнем мы с
описания входящих в нее команд.
Директивы
.include
Присоединение к текущему тексту программы другого программного
текста. Подобный прием используется практически во всех суще-
ствующих языках программирования. При составлении программ
часто бывает как, что в совершенно разных программах приходится
применять абсолютно одинаковые профаммные фрагменты. Для того,
чтобы не переписывать эти фрагменты из программы в программу,
их принято оформлять В виде отдельного файла с таким расчетом,
чтобы этот файл могли использовать все программы, где этот фраг-
мент потребуется.
23


В языке Ассемблер для присоединения фрагмента к программе ис-
пользуется псевдооператор include. В качестве параметра для этой
директивы должно быть указано имя присоединяемого файла. Если
такой оператор поставить в любом месте программы, то содержащийся
В присоединяемом файле фрагмент в процессе трансляции как бы
вставляется в то самое место, где находится оператор. Например, в
программе на листинге 1.1 в строке 1 в основной текст программы
вставляется текст из файла tn2313def. inc.
Кстати, подробнее об этом файле. Файл tn2 313de f. inc — это файл
описаний. Он содержит описание всех регистров и некоторых других
параметров микроконтроллера ATiny2313. Это описание понадобится
нам для того, чтобы в программе мы могли обращаться к каждому
регистру по его имени. О том, как делаются такие описания, мы по-
говорим при рассмотрении конкретных программ.
.list
Включение генерации листинга. В данном случае листинг—это специ-
альный файл, В котором отражается весь ход трансляции программы.
Такой листинг повторяет весь текст вашей программы, включая все
присоединенные фрагменты. Против каждой строки программы,
содержащей реальную команду, помещаются соответствующие ей
машинные коды. Там же показываются все найденные в процессе
трансляции ошибки. По умолчанию листинг не формируется. Если
вам нужен листинг, включите данную команду в вашу программу.
.def
Макроопределение. Эта команда позволяет присваивать различным
регистрам микроконтроллера любые осмысленные имена, упрощаю-
щие чтение и понимание текста программы. В нашем случае нам по-
надобится один регистр для временного хранения различных величин.
Выберем для этой цели регистр г1б и присвоим ему наименование
temp от английского слова temporary — временный.
Данная команда выполняется в строке 3 (см. листинг 1.1). Теперь
В любом месте программы вместо имени г16 можно применять имя
temp. Вы спросите: а зачем это нужно? Да для наглядности и читае-
мости программы. В данной программе мы будем использовать лишь
один регистр, и преимущества такого переименования здесь не очень
видны. Но представьте, что вы используете множество разных регист-
ров для хранения самых разных величин. В этом случае присвоение
24


осмысленного имени очень облегчает программирование. Скоро вы
сами В этом убедитесь. Кстати, именно таким образом определены
имена всех стандартных регистров в файле tn2313def. inc.
.cseg
Псевдооператор выбора программного сегмента памяти. О чем идет
речь? Как уже говорилось, микроконтроллер для хранения данных
имеет три вида памяти: память программ (Flash), оперативную память
(SRAM) и энергонезависимую память данных (EEPROM). Программа
на Ассемблере должна работать с любым из этих трех видов памя-
ти. Для этого В Ассемблере существует понятие «сегмент памяти».
Существуют директивы, объявляющие каждый такой сегмент:
? сегмент кода (памяти программ) cseg;
? сегмент данных (ОЗУ) dseg;
? сегмент EEPROM eseg.
После объявления каждого такого сегмента он становится текущим.
Это значит, что все последующие операторы относятся исключитель-
но К объявленному сегменту. Объявленный сегмент будет оставаться
текущим до тех пор^ пока не будет объявлен какой-либо другой
сегмент.
Только В сегменте кода Ассемблер описывает команды, которые
затем В виде кодов будут записаны в память программ. В остальных
двух сегментах используются директивы распределения памяти и
директивы описания данных. Ну, к сегментам dseg и eseg мы еще
вернемся. Сейчас же подробнее рассмотрим сегмент cseg.
Так как команды в программной памяти должны располагаться по
порядку, одна за другой, то их размещение удобно автоматизировать.
Программист не указывает, по какому адресу в памяти должна быть
расположена та либо иная команда. Программист просто последова-
тельно пишет команды.
А уже транслятор автоматически размещает их в памяти. Для этого
используется понятие «указатель текущего адреса». Указатель текущего
адреса не имеет отношения к регистру адреса микроконтроллера и
вообще физически не существует. Это просто понятие, используемое
В языке Ассемблер. Указатель помогает транслятору разместить все
команды программы по ячейкам памяти.
По умолчанию считается, что в начале программы значение текущего
указателя рано нулю. Поэтому первая же команда программы будет
25


размещена по нулевому адресу. По мере трансляции программы
указатель смещается в сторону увеличения адреса. Если команда
имеет длину в один байт, то после ее трансляции указатель смещается
на одну ячейку. Если команда состоит из двух байтов, на две. Таким
образом, размещаются все команды программы.
.org
Принудительное позиционирование указателя текущего адреса.
Иногда необходимо разместить какой-либо фрагмент программы
В программной памяти не сразу после предыдущего фрагмента, а в
конкретном месте программной памяти. Например, начиная с ка-
кого-нибудь заранее определенного адреса. Для этого используют
директиву org.
Она позволяет принудительно изменить значение указателя текущего
адреса. Оператор org имеет всего один параметр — новое значение
указателя адреса. К примеру, команда . org 0x10 установит ука-
затель на адрес 0x10. Транслятор автоматически следит, чтобы при
перемещении указателя ваши фрагменты программы не налезали друг
на друга. В случае несоблюдения этого условия транслятор выдает
сообщение об ошибке.
В нашей программе команда позиционирования указателя применя-
ется всего один раз. В строке 5 указатель устанавливается на нулевой
адрес. В данном случае директива org имеет чисто декларативное
значение, так как в начале программы значение указателя и так равно
нулю.
Операторы
Idi
Загрузка в РОН числовой константы, В строке б программы
(Листинг 1.1) при помощи этой команды в регистр temp (г 16) запи-
сывается числовая константа, равная максимальному адресу ОЗУ.
Эта константа имеет имя RAMEND. Ее значение описано в файле
tn2 313de f. inc. В нашем случае (для микроконтроллера ATiny2313)
значение RAMEND равно $7F.
26


Как можно видеть из листинга 1.1, оператор Idi имеет два параметра:
? первый параметр — это имя РОН, куда помещается наша кон-
станта;
? второй параметр — значение этой константы.
Обратите внимание, что в команде сначала записывается приемник
информации, затем ее источник. Такой же порядок вы увидите в
любой другой команде, имеющей два операнда. Это общее правило
для языка Ассемблер.
out
Вывод содержимого РОН в регистр ввода—вывода. Команда также
имеет два параметра:
? первый параметр — имя РВВ, являющегося приемником ин-
формации;
? второй параметр — имя РОН, являющегося источником.
В строке 7 программы содержимое регистра temp выводится в РВВ с
именем S PL.
in
Ввод информации из регистра ввода—вывода. Имеет два параметра.
Параметры те же, что и в предыдущем случае, но источник и прием-
ник меняются местами. В строке 19 программы содержимое регистра
PORTD помещается в регистр temp.
rjmp
Команда безусловного перехода. Команда имеет всего один параметр —
адрес перехода. В строке 21 программы оператор безусловного перехода
передает управление на строку, помеченную меткой main. То есть на
строку 19. Данная строка демонстрирует использование метки.
На самом деле в качестве параметра оператора rjmp должен выступать
так называемый относительный адрес перехода. То есть, число байт.
27


на которое нужно сместиться вверх или вниз от текущего адреса.
Направление смещения (вверх или вниз) — это знак числа. Он опре-
деляется старшим битом. Язык Ассемблера избавляет программиста
от необходимости подсчета величины смещения. Достаточно в нужной
строке программы поставить метку, а в качестве адреса перехода ука-
зать ее имя, и транслятор сам вычислит значение этого параметра.


При использовании команды г jmp существует одно ограничение.
Соответствующая команда микроконтроллера кодируется при по-
мощи одного шестнадцатиразрядного слова. Для указания величины
смещения она использует всего двенадцать разрядов. Поэтому такая
команда может вызвать переход в пределах ±2 Кбайт. Если вы распо-
ложите метку слишком далеко от оператора г j mp, то при трансляции
программы это вызовет сообщение об ошибке.
Описание программы (листинг 1.1)
Текст профаммы начинается шапкой с названием программы. Шапка
представляет собой несколько строк комментариев. Шапка в нача-
ле программы помогает отличать программы друг от друга. Кроме
названия программы, в шапку можно поместить ее версию, а также
дату написания.
Самая первая команда программы — это псевдокоманда include,
которая присоединяет к основному тексту программы файл описаний
(см. листинг 1.1 строка 1). В стандартном пакете AVR-Studio имеется
целый набор подобных файлов описаний. Для каждого микроконт-
роллера серии AVR свой отдельный файл. Все стандартные файлы
описаний находятся в директории «C:\Program Files\Atmel\AVR Tools\
AvrAssembler\Appnotes\». Профаммисту нужно лишь выбрать нужный
файл и ВКЛЮЧИТЬ подобную строку в свою программу. Учтите, что без
присоединения файла описаний дальнейшая программа работать не
будет.
Для микроконтроллера ATiny2313 файл описаний имеет название
tn2313def. inc. Если файл описаний находится в указанной выше
директории, то в команде inc lude достаточно лишь указать его пол-
ное имя (с расширением). Указывать полный путь необязательно.
Назначение команды .list (строка 2), надеюсь, у вас уже не вызывает
вопросов. Остановимся на команде макроопределения (строка 3). Эта
команда, как уже говорилось, присваивает регистру г 16 имя temp.
Дальше в программе регистр temp используется для временного хра-
нения промежуточных величин. Уместно задаться вопросом: почему
выбран именно г1б, а, к примеру, не г О? Это становится понятно,
если вспомнить, что регистры, начиная с гО и заканчивая г 15, имеют
меньше возможностей. Например, в строке 14 программы регистр
temp используется в команде Idi. Однако команда Idi не работает с
регистрами г0-г15. Именно по этой причине мы и выбрали г16.
29


Следующие две команды (строки 4, 5) подробно описаны в начале
этого раздела. Они служат для выбора программного сегмента памяти
и установки начального значения указателя.
В строках б и 7 производится инициализация стека. В регистр стека
SPL записывается адрес его вершины. В качестве адреса выбран самый
верхний адрес ОЗУ. Для обозначения этого адреса в данной версии
Ассемблера существует специальная константа с именем RAMEND.
Значение этой константы определяется в файле описаний (в нашем
случае в файле tn2313def. inc). Для микроконтроллера Atiny2313
константа RAMEND равна OxDF.
Одной строкой записать константу в регистр стека невозможно, так
как В системе команд микроконтроллеров AVR отсутствует подоб-
ная команда. Отсутствующую команду мы заменяем двумя другими.
И тут нам пригодится регистр temp. Он послужит в данном случае
передаточным звеном. Сначала константа RAMEND помещается в
регистр temp (строка б), а затем уже содержимое temp помещается
В регистр SPL (строка 7).
В строках 8—12 производится настройка портов ввода—вывода.
Ранее мы уже договорились, что порт PD у нас будет работать на ввод,
а порт РВ — на вывод. Для выбора нужного направления передачи
информации запишем управляющие коды в соответствующие реги-
стры DDRx. Во все разряды регистра DDRD запишем нули (настройка
порта PD на ввод), а во все разряды регистра DDRB запишем единицы
(настройка порта РВ на вывод). Кроме того, нам нужно включить
внутренние нагрузочные резисторы порта PD. Для этого мы запишем
единицы (то есть число OxFF) во все разряды регистра PORTD. И, на-
конец, В момент старта программы желательно погасить светодиод.
Для этого мы запишем единицы в разряды порта РВ.
Все описанные выше действия по настройке порта также выполня-
ются с использованием промежуточного регистра temp. Сначала в
него помещается ноль (строка 8). Ноль записывается только в регистр
DDRD (строка 9). Затем в регистр temp помещается число OxFF
(строка 10). Это число по очереди записывается в регистры DDRB,
PORTB, PORTD (строки 11,12,13).
Строки 14 и 15 включены в программу для перестраховки. Дело в том,
что встроенный компаратор микроконтроллера после системного
сброса остается включен. И хотя прерывания при этом отключены
и срабатывание компаратора не может повлиять на работу нашей
программы, мы все же отключим компаратор. Именно это и делается
В строках 14 и 15.
30


Здесь уже знакомым нам способом с использованием регистра temp
производится запись константы 0x80 в регистр ACSR. Регистр ACSR
предназначен для управления режимами работы компаратора, а кон-
станта 0x80, записанная в этот регистр, отключает компаратор.
Настройкой компаратора заканчивается подготовительная часть
программы. Подготовительная часть занимает строки 1—15 и выпол-
няется всего один раз после включения питания или после системного
сброса.
Строки 16—18 составляет основной цикл программы.
Определение. Основной цикл — это часть программы, которая
повторяется многократно и выполняет все основные
действия.
В нашем случае, согласно алгоритму, действия программы состоят в
том, чтобы прочитать состояние кнопки и перенести его на светодиод.
Есть много способов перенести содержимое младшего разряда порта
PD В младший разряд порта РВ. В нашем случае реализован самый
простой вариант. Мы просто переносим одновременно все разряды.
Для этого достаточно двух операторов.
Первый из них читает содержимое порта PD и запоминает это содер-
жимое В регистре ten^ (строка 16). Следующий оператор записывает
это число В порт РВ (строка 17). Завершает основной цикл программы
оператор безусловного перехода (строка 18). Он передает управление
по метке main.
В результате три оператора, составляющие тело цикла, повторяются бес-
конечно. Благодаря этому бесконечному циклу все изменения порта PD
тут же попадают в порт РВ. По этой причине, если кнопка S1 не нажата,
логическая единица со входа PDO за один проход цикла передается на
выход РВО. И светодиод не светится. При нажатии кнопки S1 логический
ноль со входа PDO поступает на выход РВО, и светодиод загорается.
Эта же самая профамма без каких-либо изменений может обслуживать
до семи кнопок и такое же количество светодиодов. Дополнительные
КНОПКИ подключаются к линиям PD1—PD6, а дополнительные све-
тодиоды (каждый со своим токоограничивающим резистором) к
выходам РВ1—РВ7. При этом каждая кнопка будет управлять своим
собственным светодиодом. Такое стало возможным потому, что все
выводы каждого из двух портов мы настроили одинаково (смотри
строки 8—13).
31


Программа на языке СИ
Для создания программ на языке СИ мы будем использовать про-
граммную среду CodeVisionAVR. Это среда специально предназна-
чена для разработки программ на языке СИ для микроконтроллеров
серии AVR. Среда CodeVisionAVR не имеет своего отладчика, но
позволяет отлаживать программы, используя возможности системы
AVR Studio.
Отличительной особенностью системы CodeVisionAVR является
наличие мастера-построителя программы. Мастер-построитель
облегчает работу программисту. Он избавляет от необходимости
листать справочник и выискивать информацию о том, какой регистр
за что отвечает и какие коды нужно в него записать. Результат работы
мастера — это заготовка будущей программы, в которую включены
все команды предварительной настройки, а также заготовки всех
процедур языка СИ. Поэтому давайте сначала научимся работать с
мастером, создадим заготовку нашей программы на языке СИ, а затем
разберем подробнее все ее элементы. И уже после этого создадим из
заготовки готовую программу.
Для того, чтобы понять работу мастера, нам необходимо прояснить
несколько новых понятий. Программа CodeVisionAVR, как и любая
современная среда программирования, работает не просто с текстом
программы, а с так называемым Проектом.
Определение. Проект, кроме текста программы, содержит ряд вспо-
могательных файлов, содержащих дополнительную
информацию, такую, как тип используемого процес-
сора, тактовую частоту кварца и т. п. Эта инфор-
мация необходима в процессе трансляции и отладки
программ. За исключением текста программы, все
остальные файлы проекта создаются и изменяются
автоматически.
Задача программиста лишь написать текст программы, для которого
В проекте отводится отдельный файл с расширением «с». Например,
«ргоЬа.с».
При помощи мастера мы будем создавать новый проект. В дальней-
шем мы еще рассмотрим подробно процесс установки и работу с
программной средой CodeVisionAVR. Сейчас же считаем, что она
установлена и запущена.
32


Для создания нового проекта выберем в меню «File» пункт «New».
Откроется небольшой диалог, в котором вы должны выбрать тип
создаваемого файла. Предлагается два варианта:
? «Source» (Исходный текст программы);
? «Project» (Проект).
Выбираем Project и нажимаем «Ок». Появляется окно с вопро-
сом «You are about to create a new project. Do you want to use the
CodeWizardAVR?». В переводе на русский это означает: «Вы
создаете новый проект. Будете ли вы использовать построитель
CodeWizardAVR?». Мы договорились, что будем, поэтому выбира-
ем «Yes», после чего открывается окно построителя (см. рис. 1.3).
Как видите, это окно имеет множество вкладок, каждая из которых
содержит элементы выбора режимов.
Все эти управляющие элементы позволяют настроить параметры
создаваемой заготовки программы. Сразу после запуска мастера все
параметры принимают значения по умолчанию (все внутренние уст-
ройства выключены, а все порты ввода—вывода настроены на ввод,
внутренние нагрузочные резисторы отключены). Это соответствует
начальному состоянию микроконтроллера непосредственно после
системного сброса.


пройдемся немного по вкладкам мастера и выберем необходимые
нам параметры. Те же параметры, которые нам не нужны, мы трогать
пока не будем (оставим значения по умолчанию).
Первая вкладка называется «Chip». На этой вкладке мы можем
выбрать общие параметры проекта. Используя выпадающий список
«Chip», выберем тип микроконтроллера. Для этого щелкаем мышью
по окошку и В выпавшем списке выбираем ATiny2313.
при помощи поля «Clock» выбираем частоту кварцевого резонатора.
В нашем случае она должна быть равна 4 МГц. При помощи поля
«Crystal Oscillator Divider» выбирается коэффициент деления такто-
вого генератора. Этот параметр требует пояснений. Дело в том, что
выбранный нами микроконтроллер имеет систему предварительного
деления тактовых импульсов. Если частота тактового генератора
нас не устраивает, мы можем поделить ее, и микроконтроллер будет
работать на другой, более низкой частоте. Коэффициент деления
может изменяться от 1 до 256. Мы выберем его равным единице (без
деления). То есть оставим значение по умолчанию.
Без изменений оставим флажок «Check Reset Source» (Проверка источника
сигнала сброса). Не будем углубляться в его назначение. При желании вы
все поймете, прочитав [4]. Достаточно будет понять, что включение дан-
ного флажка добавляет к создаваемой профамме процедуру, связанную с
определением источника сигнала системного сброса.


Покончив с общими настройками, перейдем к вкладке (Порты). Эта
вкладка позволяет настроить все имеющиеся порты ввода—вывода.
На вкладке «Ports» мы видим еще три вкладки поменьше (см. рис. 1.4).
По одной вкладке для каждого порта. Как уже говорилось выше,
порт РА В данной схеме мы не применяем. Поэтому сразу выбираем
вкладку «Port В».
На вкладке мы видим два столбца с параметрами. Столбец «Data direc-
tion» (Направление передачи данных) позволяет настроить каждую
линию порта либо на ввод, либо на вывод. По умолчанию каждый
параметр имеет значение «In» (вход). Поменяем для каждого разряда
это значение на «Out» (Выход).
Для того, чтобы поменять значение разряда, достаточно щелкнуть по
полю с надписью «In» один раз мышью, и параметр сменится на «Out».
Повторный щелчок заставит вернуться к «In». Каждое поле столбца
«Data direction» определяет, какое значение будет присвоено соответ-
ствующему разряду регистра DDRB в нашей будущей программе.


определяет, какое значение будет присвоено каждому из разрядов
регистра PORTB. В нашем случае порт РВ работает на вывод. Поэтому
содержимое регистра PORTB определяет выходное значение всех
разрядов порта. По умолчанию все они имеют значение «О». Но по
условиям нашей задачи они должны быть равны единице (при старте
программы светодиод должен быть отключен). Поэтому изменим
все нули на единицы. Для этого также достаточно простого щелчка
мыши. В результате всех операций вкладка «Port В» будет выглядеть
так, как это показано на рис. 1.4.
Теперь перейдем к настройке последнего порта. Для этого выберем
вкладку «Potr D». На вкладке мы увидим такие же органы управле-
ния, как на вкладке «Port В» (см. рис. 1.5). По условиям задачи порт
PD микроконтроллера должен работать на ввод. Поэтому состояние
элементов первого столбца мы не меняем.
Однако не забывайте, что нам нужно включить внутренние нагрузоч-
ные резисторы для каждого из входов. Для этого изменим значения
элементов второго столбца. Так как порт PD работает в режиме ввода,
элементы в столбце «Pullup / Output Value» принимают значение «Т»
или «Р».
«Т» (Terminate) означает отсутствие внутренней нагрузки, а «Р»
(Pull-up) означает: нагрузка включена. Включим нагрузку для каж-
дого разряда порта PD, изменив при помощи мыши значение поля с
«Т» на «Р». В результате элементы управления будут выглядеть так,
как показано на рис. 1.5.
Для данной простейшей программы настройки можно считать окон-
ченными. Остальные системы микроконтроллера нам пока не нужны.
Оставим их настройки без изменений.
Воспользуемся еще одним полезным свойством мастера программ.
Откроем вкладку «Project Information» (см. рис. 1.6). В поле «Project
Name» вы можете занести название вашего проекта. Поле «Version»
предназначено для номера версии. В поле «Date» помещают дату
разработки программы. В полях «Author» и «Company» помеща-
ется, соответственно, имя автора и название компании. В поле
«Comments:» можно поместить любые необходимые комментарии.
Вся эта информация будет автоматически помещена в заголовок
будущей программы.
36


После того, как все параметры выставлены, приступаем непос-
редственно К процессу генерации программы. Для этого выбираем в
меню «File» нашего мастера пункт «Generate, Save and Exit», как это
показано на рис. 1.7. Процесс генерации начнется с запроса имени
файла будущей программы. Для этого откроется стандартный диалог
сохранения файла, в котором вы сначала должны выбрать каталог, а
затем указать имя файла программы. Что касается каталога, то вам
нужно самостоятельно выбрать каталог, где будет размещен весь
ваш проект.
Рекомендуется для каждого проекта создавать свой отдельный ката-
лог. В нашем случае самое простое — назвать каталог именем «Progl».
А вернее, выбрать что-то вроде «C:\AVR\C\Progl\». Если каталог еще
не создан, то вы можете создать его прямо в диалоге создания файла.
Файлу рекомендую присвоить то же самое имя: «Progl».
Однако ВЫ можете выбрать имя по своему усмотрению. Для заверше-
ния процесса создания файла нажмите кнопку «Сохранить». В резуль-
тате на ваш жесткий диск запишется файл «Progl.с», в который будет
помещен созданный построителем текст заготовки вашей программы.
Расширение «.с» набирать не нужно. Оно присваивается автоматиче-
ски. Это стандартное расширение для программ на языке СИ.
Однако процесс генерации проекта на этом не заканчивается. Сразу
после того, как файл программы будет записан, откроется новый
диалог, который запросит имя для файла проекта. Файл проекта
предназначен для хранения параметров конкретного проекта.
Кроме типа используемой микросхемы и частоты задающего гене-
ратора, файл проекта используется для хранения вспомогательной
информации, такой как наличие и расположение точек останова,
закладок и т. д. Подробнее о закладках и точках останова мы узнаем,
когда будем изучать работу отладчика (см. раздел 5.1.5). В качестве
имени файла проекта удобнее всего использовать то же самое имя,
что и для текста программы. То есть «Progl». Файлу проекта автома-
тически присваивается расширение «.prj».
Когда файл проекта будет записан, диалог записи файла откроется
В третий раз. Теперь нам предложат выбрать имя для файла данных
построителя. В этот файл будут записаны текущие значения всех па-
раметров, мастера. То есть значения всех управляющих элементов со
всех его вкладок. И те, которые мы изменили в процессе настройки,
и те, которые остались без изменений (по умолчанию).
Эти данные могут понадобиться, если потребуется заново пересоздать
проект. Используя файл данных построителя, вы можете в любой
38


момент восстановить значения всех его элементов, немного подкор-
ректировать их и создать новый проект. Файлу данных построителя
присвоим то же самое имя, что обоим предыдущим («Progl»). Новый
файл получит расширение «.cwp» (Code Wizard Project).
После того, как и этот файл будет записан, процесс генерации проекта
завершается. На экране появляются два новых окна. В одном окне
открывается содержимое файла «Progl.c». Втрое окно — это файл
комментариев. Сюда вы можете записать, а затем сохранить на диске
любые замечания по вашей программе. В дальнейшем они всегда
будут у вас перед глазами.
Посмотрим теперь, что же сформировал наш построитель. Текст
программы, полученной описанным выше образом, дополненный
русскоязычными комментариями, приведен в листинге 1.2.
Все русскоязычные комментарии дублируют соответствующие
англоязычные комментарии, автоматически созданные в процессе
генерации программы. Кроме новых комментариев, в программу
добавлена только одна дополнительная строка (строка 32). Именно
она превращает созданную построителем заготовку в законченную
программу. Итак, мы получили программу на языке СИ.
Работа программы, написанной на языке Си
Теперь наша задача—разобраться, как программа работает. Именно
этим мы сейчас и займемся. Но сначала небольшое введение в новый
для нас ЯЗЫК СИ.
Программа на языке СИ, в отличие от Ассемблера, гораздо более
абстрагирована от системы команд микроконтроллера. Основные
операторы языка СИ вовсе не привязаны к командам микроконтрол-
лера. Для реализации всего одной команды на языке СИ на самом
деле используется не Ьдна, а несколько команд микроконтроллера.
Иногда даже целая небольшая программа.
В результате облегчается труд программиста, так как он теперь рабо-
тает с более крупными категориями. Ему не приходится вдаваться в
мелкие подробности, и он может сосредоточиться на главном. Язык
си так же, как и другие языки программирования, состоит из команд.
Для записи каждой команды СИ использует свои операторы и псев-
дооператоры. Но форма написания команд в программе приближена
К форме, прршятой В математике. Сейчас вы в этом убедитесь сами.
39


В языке си для хранения различных данных используются перемен-
ные. Понятие «переменная» в языке СИ аналогично одноименному
математическому понятию. Каждая переменная имеет свое имя, и ей
можно присваивать различные значенрш. Используя переменные, мож-
но строить различные выражения. Каждое выражения представляет
собой одну или несколько переменных и числовых констаьгг, связанных
арифметическими и (или) логическими операциями. Например:


Приведенные примеры — это простейшие выражения, каждое из
которых состоит всего их двух членов. Язык СИ допускает выражения
практически любой сложности.
В языке си переменные делятся на типы. Переменная каждого типа
может принимать значения из одного определенного диапазона (см,
табл. 1.1). Например:
? переменная типа char — это только целые числа;
? переменная типа float — вещественные числа (десятичная
дробь) и Т. д.
Использование переменных нескольких фиксированных типов — этс
отличительная особенность любого языка высокого уровня. Разные
версии языка СИ поддерживают различное количество типов пере-
менных. Версия СИ, используемая в CodeVisionAVR, поддерживае!
тринадцать типов переменных (см. табл. 1.1).
В языке си любая переменная, прежде чем она будет использована,
должна быть описана. При описании задается ее тип. В дальнейшем
диапазон принимаемых значений должен строго соответствовать вы-
бранному типу переменной. Описание переменной и задание fee типа
необходимы потому, что оттранслированная с языка СИ программа
вьщеляет для хранения значений каждой переменной определенные
ресурсы памяти.


Это могут быть ячейки ОЗУ, регистры общего назначения или даже
ячейки EEPROM или Flash-памяти (памяти программ). В зависимости
от заданного типа, вьщеляется различное количество ячеек для каждой
конкретной переменной. Косвенно о количестве выделяемых ячеек
можно судить по содержимому графы «Количество бит» табл. 1.1.
Описывая переменную, мы сообщаем транслятору, сколько ячеек
выделять и как затем интерпретировать их содержимое. Посмотрим,
как выглядит строка описания переменной в программе. Она пред-
ставляет собой запись следующего вида:
Тип Имя;
где «Тип» — это ТИП переменной, а «Имя» — ее имя.
Имя переменной выбирает программист. Принцип формирования
имен В языке СИ не отличается от подобного принципа в языке
Ассемблер. Допускается использование только латинских букв, цифр
исимвола подчеркивания. Начинаться имя должно с буквы или сим-
вола подчеркивания.
Кроме арифметических и логических выражений язык СИ использует
функции.
Определение. Функция в языке СИ— это аналог соответствую-
щего математического понятия. Функция получает
одно или несколько значений в качестве параметров,
производит над ними некие вычисления и возвращает
результат.
Правда, В отличие от математических функций, функции языка СИ не
всегда имеют входные значения и даже не обязательно возвращают
результат. Далее на конкретных примерах мы увидим, как и почему
это происходит.
Вообще, роль функций в языке СИ огромная. Программа на языке
си просто-напросто состоит из одной или нескольких функций.
Каждая функция имеет свое имя и описание. По имени производится
обращение к функции. Описание определяет выполняемые функцией
действия и преобразования. Вот как выглядит описание функции в
программе СИ:
ТИП Name (список параметров)
{
тело функции
}
43


Здесь Name — это имя функции. Имя для функции выбирается по
тем же правилам, что и для переменной. При описании функции
перед ее именем положено указать тип возвращаемого значения.
Это необходимо транслятору, так как для возвращаемого значения
он тоже резервирует ячейки.
Если перед именем функции вместо типа возвращаемого значения
записать слово void, то это будет означать, что данная функция не
возвращает никаких значений. В круглых скобках после имени функ-
ции записывается список передаваемых в нее параметров.
Функция может иметь любое количество параметров. Если парамет-
ров два и более, то они записываются через запятую. Перед именем
каждого параметра также должен быть указан его тип. Если у функции
нет параметров, то в скобках вместо списка параметров должно стоять
слово void. В фигурных скобках размещается тело функции.
Определение. Тело функции — это набор операторов на языке СИ,
выполняющих некие действия, В конце каждого опе-
ратора ставится точка с запятой. Если функция
небольшая, то ее можно записать в одну строку.
В этом случае операторы, составляющие тело функции, разделяет
только точка с запятой. Вот пример такой записи:
тип Name (список параметров) { тело функции }
Любая программа на языке СИ должна обязательно содержать
одну главную функцию. Главная функция должна иметь имя main.
Выполнение программы всегда начинается с выполнения функции
main. В нашем случае (см. листинг 1.2) описание функции main на-
чинается со строки 2 и заканчивается в конце программы. Функция
main В данной версии языка СИ никогда не имеет параметров и
никогда не возвращает никакого значения.
Тело функции, кроме команд, может содержать описание переменных.
Все переменные должны быть описаны в самом начале функции, до
первого оператора. Такие переменные могут быть использованы толь-
ко В той функции, В начале которой они описаны. Вне этой функции
данной переменной как бы не существует.
Если ВЫ объявите переменную в одной функции, а примените ее в
другой, то транслятор выдаст сообщение об ошибке. Это дает возмож-
44


ность объявлять внутри разных функций переменные с одинаковыми
именами и использовать их независимо друг от друга.
Определение. Переменные, объявленные внутри функций, называются
локальными. При написании программ иногда необхо-
дим другой порядок использования переменных. Иногда
нужны переменные, которые могут работать сразу
со всеми функциями. Такие переменные называются
глобальными переменными.
Глобальная переменная объявляется не внутри функций, а в начале
программы, еще до описания самой первой функции. Не спешите
без необходимости делать переменную глобальной. Если программа
достаточно большая, то можно случайно присвоить двум разным
переменным одно и то же имя, что приведет к ошибке. Такую ошибку
очень трудно найти.
Для того, чтобы все вышесказанное было понятнее, обратимся к кон-
кретному примеру—программе (листинг 1.2). А начнем мы изучение
этой программы с описания нам пока неизвестных используемых там
команд.
include
Оператор присоединения внешних файлов. Данный оператор вы-
полняет точно такую же роль, что и аналогичный оператор в языке
Ассемблер. В строке 1 программы (листинг 1.2) этот оператор присое-
диняет К основному тексту программы стандартный текст описаний
для микроконтроллера ATtiny2313.
while
Оператор цикла. Форма написания команды whi 1 е очень похожа на
форму описания функции. В общем случае команда while выглядит
следующим образом:
while (условие)
{
тело цикла
};
45


Перевод английского слова whi 1е — «пока». Эта команда организует
ЦИКЛ, многократно повторяя тело цикла до тех пор, пока выполняется
«условие», то есть пока выражение в скобках является истинным. В
языке си принято считать, что выражение истинно, если оно не равно
нулю, и ложно, если равно.
Определение. Тело цикла — это ряд любых операторов языка СИ. Как
и любая другая команда, вся конструкция whi 1 е долж-
на заканчиваться символом «точка с запятой».
В программе на листинге 1.2 оператор while вы можете видеть в
строке 31. В качестве условия в этом операторе используется просто
число 1. Так как 1 — не ноль, то такое условие всегда истинно. Такой
прием позволяет создавать бесконечные циклы. Это значит, что цикл,
начинающийся в строке 31, будет выполняться бесконечно. Тело цикла
составляет единственная команда (строка 32).
Комментарии
В программе на языке СИ так же, как и в Ассемблере, широко ис-
пользуются комментарии. В языке СИ принято два способа написа-
ния комментариев. Первый способ — использование специальных
обозначений начала и конца комментария. Начало комментария
помечается парой символов /*, а конец комментария символами */.
Это выглядит следующим образом:
/* Комментарий */
Причем комментарий, вьщеленный таким образом, может занимать не
одну, а несколько строк. В листинге 1.2 шапка программы выполнена
В виде комментария, который записан именно таким образом. Второй
способ написания комментария — двойная наклонная черта (//).
В этом случае комментарий начинается сразу после двойной наклон-
ной черты и заканчивается в конце текущей строки. В листинге 1.2
такой способ применяется по всему тексту программы.
46


Описание программы (листинг 1.2)
Как уже говорилось, текст программы, который вы видите в листин-
ге 1.2, В основном сформироэан автоматически. Большую часть про-
граммы занимает функция main. Она начинается в строке 2 и закан-
чивается В конце программы. Вся программа снабжена подробными
комментариями, которые также сформированы автоматически.
Исключения составляют все русскоязычные комментарий, которые
я добавил вручную, и одна строка в конце программы (строка 32).
Начинается программа с заголовка. В начале заголовка мастер
поместил информацию о том, что программа создана при помощи
CodeWizardAVR. Далее в заголовок включен блок информации из
вкладки «Project Information». Эту информацию мы с вами набирали
собственноручно. Далее заголовок сообщает тип процессора, его
тактовую частоту, модель памяти (Tiny — означает малая модель),
размер используемой внешней памяти и размер стека.
В строке 1 находится команда include, присоединяющая файл
описаний. После команды include мастер поместил сообщение для
программиста. Сообщение предупреждает о том, что именно в этом
месте программисту нужно поместить описание всех глобальных
переменных (если, конечно, они вам понадобятся). В данном кон-
кретном случае глобальные переменные нам не нужны. Поэтому мы
добавлять их не будем.
Теперь перейдем К функции main. Функция main содержит в себе
набор команд, настройки системы (строки 3—30) и заготовку главного
цикла программы (строка 31).
Определение. Настройка системы — это запись требуемых значений
во все управляющие регистры микроконтроллера,
В программе на Ассемблере (листинг 1.1) мы тоже производили по-
добную настройку. Однако там мы ограничились инициализацией
портов ввода—вывода и отключением компаратора. Состояние всех
остальных служебных регистров микроконтроллера программа на
Ассемблере не меняла. То есть оставляла значения по умолчанию.
На языке СИ можно было бы поступить точно так же. Но мастер-по-
строитель программы поступает по-другому. Он присваивает значения
всем без исключения служебным регистрам. И тем, значения которых
должны отличаться от значений по умолчанию, и тем, значения ко-
торых не изменяются.
47


В последнем случае регистру присваивается то же самое значение,
какое он и так имеет после системного сброса. Такие, на первый
взгляд, избыточные действия имеют свой смысл. Они гарантируют
правильную работу программы в том случае, если в результате ошибки
В программе управление будет передано на ее начало.
Лишние команды при желании можно убрать. В нашем случае дос-
таточно оставить лишь команды инициализации портов (строки 7,8
для порта РВ и строки 9,10 для порта PD). А также команду инициа-
лизации компаратора (строка 30).
Теперь посмотрим, как же происходит присвоение значений. В строке 3
регистру CLKPR присваивается значение 0x80. Для присвоения значе-
ния используется хорошо знакомый нам символ «=» (равно). В языке
си такой символ называется оператором присвоения. Таким же самым
образом присваиваются значения и всем остальным регистрам.
Что касается настройки портов РВ и PD, то в строках 7,8 регистрам
PORTB и DDRB присваивается значение 0x7F. А в строках 9,10 в
регистр PORTD записывается 0x7F, а в регистр DDRD — ноль. Если
ВЫ помните, те же значения мы присваивали тем же самым регистрам в
программе на Ассемблере. Точнее, небольшое отличие все же есть.
В программе на Ассемблере в регистр PORTD мы записывали OxFF (в
двоичном варианте Ob 11111111). В программе на СИ в тот же регистр
мы записываем 0х7Р (ObOlllllll). Эти два числа отличаются лишь
значением старшего бита. Но для данного порта это несущественно,
так как его старший разряд незадействован. Поэтому в каждой про-
грамме мы делаем так, как это удобнее.
После инициализации всех регистров начинается основной цикл
программы (строка 31). Основной цикл — это обязательный элемент
любой программы для микроконтроллера. Поэтому мастер всегда
создает заготовку этого цикла. То есть создает цикл, тело которого
пока не содержит не одной команды.
В том месте, где программист должен расположить команды, состав-
ляющие тело этого цикла, мастер помещает специальное сообщение,
приглашающее вставить туда код программы. Оно гласит: Place your
code here (Пожалуйста, вставьте ваш код). Мы последовали этому
приглашению и вставили требуемый код (строка 32). В нашем случае
он состоит всего из одной команды. Эта команда присваивает регистру
PORTB значение регистра PORTD.
Наша программа на языке СИ готова. Выполняясь многократно в
бесконечном цикле, команда присвоения постоянно переносит со-
держимое порта PD В порт РВ, реализуя тем самым наш алгоритм.
48


1.3. Переключающийся светодиод
Постановка задачи
Как уже говорилось, предыдущая задача настолько проста, что ре-
шение ее средствами микропроцессорной техники лишено всякого
смысла. Усложним немного задачу. Заставим переключаться свето-
диод при нажатии кнопки.
Новая задача, может звучать так:
«Разработать устройство управления одним светодиодным инди-
катором при помощи одной кнопки. При каждом нажатии кнопки
светодиод должен поочередно включаться и отключаться. При пер-
вом нажатии кнопки светодиод должен включиться, при следующем
отключиться и т. д.».
Вы можете сказать, что и эта новая задача легко решаема при помощи
простейшего D-триггера. Однако все же рассмотрим, как ее можно
решить при помощи микроконтроллера.
Принципиальная схема
Так как для новой задачи, как и для предыдущей, нам необходима
всего одна кнопка и всего один светодиод, то придумывать новую
схему не имеет смысла. Применим для второй задачи уже знакомую
нам схему, показанную на рис. 1.2.
Алгоритм
Алгоритм задачи номер два начинается так же, как алгоритм нашей
первой задачи. То есть с набора команд, выполняющих инициализа-
цию системы. Так как схема и принцип работы портов ввода—вывода
не изменились, то алгоритм инициализации системы будет полностью
повторять соответствующий алгоритм из предыдущего примера.
После команд инициализации начинается основной цикл программы.
Однако действия, выполняемые основным циклом, будут немного
49


другими. Попробуем, как и в предыдущем случае, описать эти дей-
ствия словами.
1. Прочитать состояние младшего разряда порта PD (PD.O).
2. Если значение этого разряда равно единице, перейти к началу
цикла.
3. Если значение разряда PD.0 равно нулю, изменить состояние
выхода РВ.О на противоположное.
4. Перейти к началу цикла.
Итак, мы описали алгоритм словами. Причем это довольно общее
описание. Реальный алгоритм немного сложнее. Словесное описание
алгоритма не всегда удобно. Гораздо нагляднее графический способ
описания алгоритма. На рис. 1.3 алгоритм нашей работы буДущей
программы изображен в графическом виде.
Такой способ отображения информации называется графом.
Прямоугольниками обозначаются различные действия, вьшолняемые
программой. Суть выполняемого действия вписьшается внутрь такого
прямоугольника.
Допускается объединять несколько операций в один блок и обозначать
одним прямоугольником. Последовательность выполнения действий
показывается стрелками. Ромбик реализует разветвление программы.
Он представляет собой операцию выбора. Условие выбора записывает-
ся внутри ромбика. Если условие истинно, то дальнейшее выполнение
программы продолжится по пути, обозначенному словом «Да».
Если условие не выполнено, то программа пойдет по другому пути,
обозначенному стрелкой с надписью «Нет». Прямоугольником со
скругленными боками принято обозначать начало и конец алгоритма.
В нашем случае программа не имеет конца. Основной цикл программы
является бесконечным циклом.
Рассмотрим подробнее алгоритм, изображенный на рис. 1.8. Как
видно из рисунка, сразу после старта программы выполняется уста-
новка вершины стека. Следующее действие — это программирование
портов ввода—вывода. Затем начинается главный цикл программы
(обведен пунктирной линией). Внутри цикла ход выполнения про-
граммы разветвляется.
Первой операцией цикла является проверка состояния младшего
разряда порта PD (PDO). Программа сначала читает состояние этой
линии, а затем выполняет операцию сравнения. В процессе сравне-
ния значение разряда PDO проверяется на равенство единице. Если
50


Если условие не выполняется (PDO не равен единице), выполнение
программы продолжается по стрелке «Нет», где выполняется еще
одна операции сравнения. Это сравнение является частью процедуры
переключения светодиода. Для того, чтобы переключить светодиод,
мы должны проверить его текущее состояние и перевести его в про-
тивоположное.
Как ВЫ помните, светодиодом управляет младший разряд порта РВ
(РВО). Поэтому именно его мы будем проверять и изменять. Работа
алгоритма переключения светодиода предельно проста. Сначала
оператор сравнения проверяет разряд РВО на равенство единице.
Если результат проверки — истина (РВО=1), то разряд сбрасывается
В ноль (О => РВО). Если ложно, устанавливается в единицу (1 => РВО).


Сочетание символов «=>» означает операции) присвоения. Такое обо-
значение иногда используется в программировании при написании
алгоритмов. После переключения светодиода управление передается
на начало главного цикла.
Итак, наш алгоритм готов, и можно приступать к составлению про-
граммы. Но не торопитесь. Все не так просто. Приведенный выше
алгоритм хорош лишь для теоретического изучения приемов про-
граммирования. На практике же он работать не будет.
Дело В том, что микроконтроллер работает с такой скоростью, что за
время, пока человек будет удерживать кнопку в нажатом состоянии,
главный ЦИКЛ программы успеет выполниться многократно (до сотни
раз). Это произойдет даже в том случае, если человек постарается
нажать и отпустить кнопку очень быстро. При каждом проходе глав-
ного цикла программа обнаружит факт нажатия кнопки и переключит
светодиод.
В результате работа нашего устройства будет выглядеть следующим
образом. Как только кнопка будет нажата, светодиод начнет быстро
переключаться. На столько быстро, что вы даже не увидите, как он
мерцает. Это будет выглядеть как свечение в полнакала.
В момент отпускания кнопки процесс переключения остановится, и
светодиод окажется в одном из своих состояний (засветится или по-
тухнет). В каком именно состоянии он останется, зависит от момента
отпускания кнопки. А это случайная величина. Как видите, оггасанный
выше алгоритм не позволяет создать устройство, соответствующее
нашему техническому заданию.
Для того, чтобы решить данную проблему, нам необходимо усовер-
шенстБОвать наш алгоритм. Для этого в программу достаточно ввести
процедуру ожидания. Процедура ожидания приостанавливает основ-
ной цикл программы сразу после того, как произойдет переключение
светодиода. Теперь программа должна ожидать момента отпускания
кнопки. Как только кнопка окажется отпущенной, выполнение глав-
ного цикла возобновляется.
Новый, доработанный алгоритм приведен на рис. 1.9. Как видно из
рисунка, новый алгоритм дополнен всего двумя новыми операциями,
которые и реализуют цикл ожидания. Цикл ожидания добавлен после
процедуры переключения светодиода. Выполняя цикл ожидания,
программа сначала читает значение бита PDO, а затем проверяет его
на равенство единице. Если PDO не равно единице (кнопка нажата),
то цикл ожидания повторяется. Если PDO равно единице (кнопка
52


отпущена) то цикл ожидания прерывается, и управление перейдет
на начало основного цикла.
Новый алгоритм вполне работоспособен и может стать основой ре-
альной программы. Попробуем составить такую программу.


Программа на Ассемблере
Текст возможного варианта программы для второго примера приве-
ден В листинге 1.3.


В программе применены следующие новые для нас команды:
sbrc
Команда из группы условных переходов. Вызывает пропуск следую-
щей за ней команды, если соответствующий разряд РОН сброшен. У
команды два параметра. Первый параметр — имя регистра общего
назначения, второй параметр — номер проверяемого бита. В строке
17 программы (листинг 1.3) подобная команда проверяет нулевой
разряд регистра temp. Если этот разряд равен нулю, то команда, за-
писанная В строке 16, пропускается, и выполняется команда из строки
17. Если проверяемый бит равен единице, то пропуска не происходит,
и выполняется команда в строке 16.
sbrs
Команда, обратная предыдущей. Пропускает следующую команду,
если соответствующий разряд РОН установлен в единицу. Имеет
те же два параметра, что и команда sbrc В строке 26 (листинг 1.3)
подобная команда проверяет значение младшего разряда регистра
temp. Если проверяемый бит равен единице, то команда в строке 27
пропускается, и выполняется команда в строке 28. Если проверяемый
разряд равен нулю, то выполняется строка 27.
sbi
Сброс в ноль одного из разрядов порта ввода—вывода. Команда имеет
два параметра: имя порта и номер сбрасьшаемого разряда. В строке 22
(листинг 1.3) подобная команда выполняет сброс младшего разряда
порта PORTS.
сЫ
Установка в единицу одного из разрядов порта ввода—вывода. Имеет
такие же два параметра, как и предыдущая команда. В строке 24
(листинг 1.3) подобная команда устанавливает младший разряд порта
PORTS В единицу.
55


Описание программы (листинг 1.3)
Первая часть программы (строки 1—15) полностью повторяет ана-
логичную часть программы из предыдущего примера (листинг 1.1).
И это неудивительно, так как алгоритм инициализации не изменился.
Зато значительно усложнился основной цикл программы. Теперь он
значительно вырос по объему и занимает строки 16—^28. В строке
16 производится чтение порта PORTD. Число, прочитанное из порта,
записывается в регистр temp.
В строке 17 производится проверка младшего разряда прочитанно-
го числа. Если младший бит равен единице (кнопка не нажата), то
управление переходит к строке 18. В строке 18 находится оператор
безусловного перехода, который передает управление по метке main,
то есть на начало цикла. Таким образом, пока кнопка не нажата, будет
выполняться короткий ЦИКЛ программы (строки 16,17 и 18).
Если кнопка нажата, младший разряд числа в регистре temp окажет-
ся равным нулю. В этом случае оператор sbrc в строке 17 передаст
управление к строке 19, где начинается модуль переключения свето-
диода. И начинается он с чтения состояния порта РВ.
В строке 20 производится проверка младшего бита считанного числа.
Если этот бит равен нулю, то строка 21 пропускается, и выполняется
строка 22. Если младший бит равен единице, то выполняется строка
21. В строке 22 оператор sbi устанавливает младший бит регистра
PORTS В единицу.
А В строке 21 находится оператор безусловного перехода, который
передает управление по метке ml на строку 24. Там оператор cbi
сбрасывает младший бит регистра FORTH в ноль. Таким образом, про-
исходит переключение в младшем разряде порта РВ. Ноль меняется
на единицу, а единица на ноль.
После переключения светодиода управление передается на строку
25. Это происходит либо при помощи команды безусловного перехода
(строка 23), либо естественным путем после строки 24.
Строки 25—^27 содержат цикл ожидания момента отпускания кноп-
ки. Цикл ожидания начинается с чтения содержимого порта PORTD
(строка 25). Прочитанное значение записывается в регистр temp.
Затем производится проверка младшего разряда прочитанного числа
(строка 26). Если этот разряд равен нулю (кнопка еще не отпущена),
выполняется строка 27 (безусловный переход на метку т2), и цикл
ожидания повторяется снова.
56


Когда при очередной проверке кнопка окажется отпущенной, пови-
нуясь команде sbrc (в строке 26), микроконтроллер пропустит строку
27 и перейдет к строке 28 * Расположенный там безусловный переход
передаст управление на начало основного цикла (по метке main).
Программа на языке СИ
Та же задача на языке СИ решается следующим образом. При помощи
построителя создаем точно такую же заготовку программы с теми
же параметрами, как и в предьщущем случае. Доработка программы
также будет сводиться к вписыванию необходимых команд в основной
ЦИКЛ программы. Однако это будут другие команды, реализующие
новый алгоритм.
Возможный вариант программы смотри в листинге 1.4. Прежде чем
мы перейдем к изучению этой программы, необходимо остановиться
на новом элементе языка СИ, который в ней применяется.
if else
Условный оператор. Этот оператор позволяет выполнять разные опе-
рации В зависимости от некоторого условия. В программе на языке
си оператор записывается следующим образом:
if (условие)
{ Набор операторов 1 }
else
{ Набор операторов 2 }
Условие — это любое логическое выражение. Если результат этого вы-
ражения — истина (не равен 0), то выполняется «Набор операторов 1».
В противном случае выполняется «Набор операторов 2».
Оба набора операторов — это любые допустимые операторы языка
си. Каждый из операторов в наборе должен оканчиваться точкой с
запятой. Добавочное слово else не обязательно. Его можно исклю-
чить вместе с набором операторов 2. Тогда, если условие ложно,
оператор не будет выполнять никаких действий.
57


Листинг 1.4


Описание программы (листинг 1.4)
Начало программы (до строки 30) сформировано автоматически и
полностью соответствует соответствующей части предыдущей про-
граммы (листинг 1.2). Я лишь немного сократил комментарии для того,
чтобы не перегружать текст программы лишней информацией.
Тело основного цикла претерпело значительные изменения. Теперь
он занимает строки 31—37. В строке 32 расположена процедура ожи-
дания нажатия кнопки. Она представляет собой пустой цикл while.
В теле цикла (две фигурные скобки) нет ни одного оператора.
Цикл не выполняет никаких действий. Он будет выполняться, пока
его условие истинно. В качестве условия выбрано равенство млад-
шего разряда регистра PORTD нулю. На языке СИ это записывается
следующим образом:
PORTD.0==1.
В языке си различают оператор равенства и оператор присвоения:
? один символ «=» означает присвоение,
запись типа А=5 означает присвоение переменной А значения 5;
? двойной символ «==» означает операцию сравнения,
запись А==5 означает проверку на равенство значений пере
менной А и константы 5.
Результат такого сравнения равен единице в случае, если А равно
ПЯТИ, и равен нулю, если это не так. Поэтому, цикл whi 1 е в строке 32
программы продолжается до тех пор, пока значение разряда PORTD.0
равно единице. То есть до тех пор, пока кнопка, подключенная к этому
разряду, остается не нажатой. Как только кнопка окажется нажатой,
цикл (строка 32) заканчивается, и программа перейдет к строке 33.
В строках 33—36 находится оператор сравнения. Он выполняет
задачу переключения светоДиода. Для этого в его условии записана
проверка младшего разряда порта РВ, то есть содержимого регистра
PINB. Разряд проверяется на равенство единице (строка 33).
Если значение разряда равно единице, то выполняется строка 34, в
которой младшему разряду регистра PORTB присваивается нулевое
значение. Если условие не выполняется (значение PINB.0 не равно
единице), выполняется строка 36, и младшему разряду PORTB
присваивается единица. Значение, записанное в регистр PORTB, не-
посредственно поступает на выход порта РВ. В результате состояние
младшего разряда порта (РВО) меняется на противоположное.
59


В строке 37 программы расположен цикл ожидания отпускания
КНОПКИ. Он аналогичен циклу в строке 32. Только условие теперь
обратное. Цикл выполняется до тех пор, пока значение PORTD.0
равно нулю. То есть пока кнопка нажата.
1.4. Боремся с дребезгом контактов
Постановка задачи
Обратимся еще раз к схеме на рис. 1.2. В схеме используется кнопка,
имеющая одну группу из двух нормально разомкнутых контактов. А
если есть контакты, значит, есть и дребезг этих контактов. В [3]
рассматривается способ борьбы с антидребезгом аппаратным спо-
собом. Теперь рассмотрим способ борьбы с дребезгом контактов
программным путем.
Итак, новая задача будет сформулирована следующим образом:
«Разработать схему управления светодиодом при помойки одной кноп-
ки. При нажатии кнопки светодиод должен изменять свое состояние
на противоположное (включен или выключен). При разработке про-
граммы принять меры для борьбы с дребезгом контактов».
Схема
Как уже говорилось, принципиальная схема остается прежняя (см.
рис. 1.2).
Алгоритм
Алгоритм нам придется доработать. Самый простой способ борь-
бы с дребезгом — введение в программу специальных задержек.
Рассмотрим это подробнее. Начнем с исходного состояния, когда
контакты кнопки разомкнуты. Программа ожидает их замыкания. В
момент замыкания возникает дребезг контактов.
Дребезг приводит к тому, что на соответствующем разряде порта
PD вместо простого перехода с единицы в ноль мы получим серию
60


Начало


импульсов. Для того, чтобы избавится от их паразитного влияния,
программа должна сработать следующим образом. Обнаружив первый
же нулевой уровень на входе, программа должна перейти в режим
ожидания. В режиме ожидания программа приостанавливает все свои
действия и просто отрабатывает задержку.
Время задержки должно быть выбрано таким образом, чтобы оно
превышало время дребезга контактов. Такую же процедуру задержки
нужно ввести в том месте программы, где она ожидает отпускания
кнопки. Для разработки нового алгоритма возьмем за основу преды-
дущий (см. рис. 1.9). Доработанный алгоритм с добавлением операций
антидребезговой задержки приведен на рис. 1.10. Как вы можете ви-
деть из рисунка, вся доработка свелась к включению двух процедур
задержки. Одной — после обнаружения факта нажатия кнопки, а
второй — после обнаружения факта ее отпускания.
Программа на Ассемблере
Для реализации нового алгоритма немного доработаем программу
(листинг 1.3). Новый вариант программы приведен ниже (лис-
тинг 1.5). В этой программе используются следующие новые для нас
операторы:
rcall
Переход к подпрограмме. У этого оператора всего один параметр —
относительный адрес начала подпрограммы. Относительный ад-
рес —это просто смещение относительно текущего адреса. Выполняя
команду real 1, микроконтроллер запоминает в стеке текущий адрес
программы из счетчика команд и переходит по адресу, определяемо-
му смещением. Такой же принцип задания адреса для перехода мы
уже встречали в команде г jmp. В строке 20 программы (листинг 1.5)
производится вызов подпрограммы задержки по адресу, соответст-
вующему метке wait.
ret
Команда выхода из подпрограммы. По этой команде микроконтроллер
извлекает из стека адрес, записанный туда при выполнении коман-
ды rcall, и осуществляет передачу управления по этому адресу. В
листинге 1.5 команду ret мы можем видеть в конце подпрограммы
wait (см. строку 37).
62


push
Запись содержимого регистра общего назначения в стек. У данного
оператора всего один операнд — имя регистра, содержимое которого
нужно поместить в стек. В строке 32 программы (листинг 1.5) в стек
помещается содержимое регистра с именем loop.
pop
Извлечение информации из стека. У этого оператора тоже всего один
операнд — имя регистра, в который помещается информация, извле-
каемая из стека. В строке 36 программы (листинг 1.5) информация
извлекается из стека и помещается в регистр loop.
dec
Уменьшение содержимого РОН. У команды один параметр — имя
регистра. Команда dec (декремент) уменьшает на единицу содер-
жимое регистра, имя которого является ее параметром. В строке
34 программы (листинг 1.5) уменьшается на единицу содержимое
регистра loop.
brne
Оператор условного перехода (переход по условию). У этого оператора
всего один параметр — относительный адрес перехода. Условие пере-
хода звучит как «не равно». Попробуем разобраться, как проверяется
это условие. И тут нам придется вспомнить о регистре состояния
микроконтроллера (SREG).
Как уже говорилось ранее, каждый бит этого регистра представляет
собой флаг. Все флаги регистра предназначены для управления рабо-
той микроконтроллера. Кроме уже известного нам флага I (глобальное
разрешение прерываний), этот регистр имеет ряд флагов, отражающих
результаты работы различных операций.
Полное описание регистра SREG смотрите [4]. В данном случае
нас интересует лишь один из таких флагов — флаг нулевого резуль-
тата (флаг Z). Этот флаг устанавливается в том случае, когда при
выполнения очередной команды результат окажется равным нулю.
Например при вычитании двух чисел, сдвиге разрядов числа или в
результате операции сравнения. В нашем случае на значение флага
влияет команда dec. (строка 34). Если в результате действия этого
63


оператора содержимое регистра окажется равным нулю, то флаг Z
устанавливается в единицу. В противном случае он сбрасывается
В ноль.
Флаг Z будет хранить записанное в него значение до тех пор, пока
какая-нибудь другая команда его не изменит. Какие из команд оказы-
вают влияние на флаг Z, а какие нет, можно узнать из приложения.
Команда brne использует флаг Z в качестве условия. Команда выпол-
няет переход только в том случае, если флаг Z сброшен. То есть когда
результат предыдущей команды неравен нулю. В нашей программе
(листинг 1.5) подобный оператор применяется в строке 35.


Описание программы (листинг 1.5)
Новый вариант программы является полной копией старой (см. лис-
тинг 1.3), В которую добавлены новые элементы, обеспечивающие
антидребезговую задержку. Так как задержка нужна в двух разных
местах программы, она оформлена в виде подпрограммы. Для фор-
мирования задержки используется один дополнительный регистр
общего назначения.
Поэтому В начале нашей новой программы (строка 4) добавлена ко-
манда описания регистра. Регистру г17 и присваивается имя loop. По-
английски слово loop означает цикл. Таким именем принято называть
переменные, применяемые для организации циклов. Не удивляйтесь,
что я употребил тут термин «переменная». В языке Ассемблер тоже
используется понятие «переменные». Так наш регистр loop можно
считать переменной с именем loop.
Запись значения в этот регистр эквивалентна присвоению значения
переменной. Также можно интерпретировать и другие операции с
регистром. Сложение содержимого двух регистров можно считать
сложением переменных, вычитание — вычитанием, и так далее.
Подпрограмма задержки расположена в строках 32—37. Первой
строке подпрограммы присвоена метка wait. Именно по этой метке
и будет вызываться подпрограмма. Опустим пока назначение команд
hush и pop (строки 32 и 36). Собственно процедура задержки рас-
3 Зак 911 65


положена в строках 33—35. Формирование задержки производится
путем многократного выполнения пустого цикла. Сначала в регистр
loop записывается некое начальное значение (строка 33). В нашем
случае оно равно 200.
Затем начинается цикл, который постепенно уменьшает значение
регистра loop до нуля (строки 34 и 35). Происходит это следующим
образом. В строке 34 содержимое регистра уменьшается на единицу,
а В строке 35 происходит проверка содержимого на ноль. Если ноль
не достигнут, то управление передается по метке wtl, и цикл повто-
ряется. Когда же содержимое loop кажется равным нулю, очередного
перехода не произойдет, и цикл задержки закончится.
Очевидно, ЧТО в нашем случае цикл задержки выполнится 200 раз.
Если обратиться к приложению, то можно узнать, что команда dec
выполняется за один такт системного генератора. Команда brne
выполняется:
? за один такт, если не вызывает перехода;
? за два такта, если вызывает переход.
Поэтому один цикл задержки будет выполняться за 3 такта. Двести
ЦИКЛОВ за 3x200=600 тактов. Тактовая частота кварцевого резонатора
у нас равна 4 МГц. Длительность одного колебания тактовой частоты
равна 1/4 = 0,25 мкс. Поэтому время, за которое будут выполнены все
200 ЦИКЛОВ задержки, равно 600x0,25 = 150 мкс. Добавьте сюда время
выполнения остальных команд подпрограммы, включая команду
вызова подпрограммы и команду возврата из подпрограммы, и вы
получите общее время задержки (162 мкс).
Максимальная задержка, которую можно сформировать при помощи
данной подпрограммы, равна (255x3x0,25)4-12=203,25 мкс. Учтите,
ЧТО В нашем случае не применяется предварительное деление частоты
тактового генератора. Если это было бы не так, то длительность вы-
полнения каждой команды нужно было бы умножать на коэффициент
деления предварительного делителя.
Теперь вернемся к двум командам работы со стеком, которые мы не
стали рассматривать вначале. Они предназначены для сохранения в
стеке и последующего восстановления содержимого регистра loop. В
начале подпрограммы (строке 32) значение loop сохраняется, а перед
выходом их подпрограммы (строка 36) — восстанавливается.
Подобный прием придает программе одно полезное свойство. После
окончания работы подпрограммы значения всех регистров микро-
контроллера остается без изменений. В данном конкретном случае
66


такое свойство ничего не дает, кроме, разве что, дополнительной
задержки. Однако в сложных программах, имеющих не одну, а не-
сколько подпрограмм, одни и те же регистры удобно использовать в
разных подпрограммах.
Те же самые регистры может использовать и основная программа.
В этом случае описанное выше полезное свойство просто необходи-
мо для правильной работы всей программы. Зная эту особенность,
программисты стараются применять подобный прием в каждой
подпрограмме, независимо от того, полезен он в данном конкретном
случае или нет.
Не исключена ситуация, когда в процессе доработки программы вам
все же придется повторно использовать какие-либо регистры. Заранее
обеспечить корректную работу вашей подпрограммы — это хороший
стиль программирования.
В соответствии с алгоритмом (рис. 1.5) подпрограмма задержки в на-
шей программе вызывается два раза. Первый раз — после окончания
цикла ожидания нажатия кнопки (строка 20). Второй раз — после
окончания цикла ожидания отпускания (строка 30).
Программа на языке СИ
с программой на языке СИ мы поступим так же, как с программой на
Ассемблере. Мы просто возьмем предыдущий вариант (листинг 1.4)
и вставим В него задержки. Для языка СИ добавить задержку в про-
грамму гораздо проще, чем для Ассемблера. Для того, чтобы ввести
задержку, мы воспользуемся стандартной библиотекой процедур
задержки.
Эта библиотека входит в состав программного комплекса
CodeVisionAVR. Название этой библиотеки delay .h. Посмотрите
на НОВЫЙ вариант программы (листинг 1.6). В строке 2 мы присое-
диняем библиотеку delay. h К тексту нашей программы. Так же,
как В строке 1 мы присоединили файл описания микросхемы. После
присоединения библиотеки в нашем распоряжении появляется не-
сколько функций, реализующих задержку. Воспользуемся одной из
них. Имя этой функции delay_us (задержка в микросекундах). Она
обеспечивает задержку в любое целое количество микросекунд.
Количестю микросекунд задержки передается в функцию в качестве пара-
метра. В строке34 профаммы (листинг 1.6) функция задержки вызьшается
67


первый раз. Она обеспечивает задержку на 200 мкс после окончания цикла
ожидания нажатия кнопки. В строке 40 такая же задержка вызьшается
после окончания цикла ожидания отпускания кнопки.
Теперь немного поговорим о функции delay__us. Данная функция
обеспечивает формирование задержки при помощи беско^1ечного
цикла. Такого же цикла, который мы применяли в программе на
Ассемблере. Но теперь нам не нужно описывать цикл в подробностях.
Достаточно применить готовую функцию.
В процессе трансляции пустой цикл формируется автоматически.
Начальное значение высчитывается, исходя из заданной величины
задержки и частоты тактового генератора, указанной при создании
проекта.


Кроме новой для нас функции, программа, показанная на листинге 1.6,
имеет еще несколько отличий от оригинала:
? В программе существенно сокращен блок команд инициализации;
? удалены все команды, созданные построителем, которые дуб-
лируют запись В регистры значений по умолчанию.
Удаление лишних команд сокращает объем программы и облегчает ее
понимание. Какие же команды были удалены? Это команды настрой-
ки тех систем, которые в данном случае не используются. Оставлены
лишь команды настройки портов В и D. А также команда настройки
аналогового компаратора.
69


1.5. Мигающий светодиод
Постановка задачи
Создадим программу с мигающим светодиодом. Сформулируем ус-
ловие следующим образом;
«Создать устройство с одним светодиодом и одной управляющей
кнопкой. Кнопка должна включать и выключать мигание светодиода.
Пока кнопка отпущена, светодиод не должен светиться. Все время,
пока кнопка нажата, светодиод должен мигать с частотой 5 Гц»,
Схема
И В этом примере мы воспользуемся уже знакомой нам схемой, изо-
браженной на рис. 1.2.
Алгоритм программы
Алгоритм такой программы тоже состоит из алгоритма начальной
установки и алгоритма основного цикла. Начальная установка в дан-
ном случае не отличается от начальной установки всех предыдущих
примеров. Алгоритм основного цикла программы можно описать
следующим образом:
1. Произвести чтение порта PD;
2. Проверить младший разряд полученного числа (если его зна-
чение равно нулю, ВКЛЮЧИТЬ алгоритм мигания);
3. Если значение младшего разряда PD равно единице, выключить
алгоритм мигания и потушить светодиод;
4. Перейти к началу основного цикла (пункт1).
Для того, чтобы выполнить все предыдущие пункты, нам нужно опи-
сать алгоритм мигания светодиода. Он будет выглядеть следующим
образом:
5. Зажечь светодиод;
6. Выдержать паузу;
7. Потушить светодиод;
8. Выдержать паузу;
9. Перейти к началу алгоритма мигания (пункт 1).
70


Программа на Ассемблере
Возможный вариант программы приведен в листинге 1.7.


Программа содержит всего одну новую для нас команду.
breq
Оператор условного перехода по условию «равно». Этот оператор —
полная противоположность оператору brne, описанному в преды-
дущем примере. Отличие этих двух операторов друг от друга в том,
что brne вызывает переход в том случае, если флаг Z установлен, а
оператор breq, напротив, вызовет переход, если Z сброшен.
Описание программы (листинг 1.7)
Для новой задачи нам пришлось создать новую подпрограмму задерж-
ки. Это произошло потому, что приведенная в предыдущем примере
подпрограмма не способна обеспечить задержку достаточно большой
длительности. Новая подпрограмма задержки использует не один,
а целых три вложенных друг в друга цикла. По этой причине нам
понадобится не один, а три вспомогательных регистра.
Поэтому В блок инициализации новой программы включены три
оператора, определяющие три вспомогательные переменные loopl,
1оор2 и 1оорЗ (строки 4,5,6). В остальном блок инициализации пол-
ностью соответствует аналогичному блоку из предьщущего примера.
Теперь он занимает строки 1—18.
Основной ЦИКЛ программы занимает строки 19—27. Он начинается с
установки единицы в младшем разряде порта РВ (строка 19). В резуль-
тате светодиод выключается. Следующая команда читает содержимое
порта PD и помещает его в регистр temp (строка 20).
В строке 21 содержимое младшего разряда полученного числа про-
веряется на равенство единице. Если младший разряд равен единице
(кнопка отпущена), то управление передается по метке main. И цикл


замыкается. Так происходит все время, пока кнопка не нажата. При
каждом проходе оператор sbi (строка 19) подтверждает единицу на
выходе РВО. Светодиод остается незажженным.
Как только кнопка будет нажата, младший бит считанного из порта PD
числа окажется равным нулю. Повинуясь команде сравнения в строке
21, микроконтроллер пропустит строку 22, и управление перейдет к
строке 23. В строке 23 начнется процедура мигания светодиода.
Она реализует один цикл мигания и работает следующим образом.
Сначала оператор сЫ (строка 23) устанавливает на выходе РВО
НИЗКИЙ логический уровень (зажигает светодиод). Затем происходит
ВЫЗОВ подпрограммы задержки (строка 24). По окончании задержки
команда sbi (строка 25) переводит разряд РВО в единицу (тушит
светодиод). И снова задержка (строка 26).
Оператор безусловного перехода (строка 27). передает управление
на начало основного цикла программы. И вся процедура повторится
сначала. Снова проверка нажатия кнопки. Если кнопка нажата, то
ЦИКЛ мигания повторяется. Если же кнопка окажется отпущенной,
продолжения мигания не произойдет. Программа потушит светодиод
и войдет В ЦИКЛ ожидания нажатия кнопки (строки 19—22).
И так с миганием мы разобрались. Теперь перейдем к подпрограм-
ме формирования задержки. Текст этой подпрограммы занимает
строки 28—44. Так как требуемая частота мигания должна быть
равна 5 Гц, подпрограмма должна обеспечивать время задержку
1/5 = 0,2 с (200 мс).
Как уже говорилось, подпрограмма представляет собой три вложен-
ных друг в друга цикла. Самый внутренний цикл организован при
помощи регистра loopl и включает в себя строки 38 и 39. Перед
началом цикла в регистр loopl записывается число 255 (строка 37).
Поэтому ЦИКЛ повторяется 255 раз. Число 255 — это самое большое
значение, которое можно записать в один восьмиразрядный регистр.
Как уже говорилось, задержка, формируемая таким циклом, может
быть лишь чуть больше, чем 200 мкс.
Для увеличения задержки организован второй цикл. Второй цикл
использует регистр 1оор2. Перед началом цикла в'этот регистр за-
писывается число 100 (строка 34). Цикл организован при помощи
оператора dec (строка 35), который последовательно уменьшает
содержимое регистра 1оор2 оператора сравнения breq (строка 36),
проверяет, не достигло ли значение регистра нуля.
Пока В 1оор2 не равно нулю, выполняется тело цикла (строки 37—40).
В тело второго цикла включен первый цикл, использующий регистр
73


loopl. Таким образом, цикл loopl выполняется при каждом проходе
цикла 1оор2. Общее суммарное количество проходов обоих циклов
будет равно 255x100 = 25500.
Но и этого недостаточно для создания нужной задержки. Даже если
начальное значение для 1оор2 мы выберем равным 255, и тогда мы
не получим искомые 200 мс. Поэтому вокруг первых двух циклов ор-
ганизован третий. Третий цикл использует регистр 1оорЗ и построен
точно так же, как второй. Перед началом работы в регистр 1оорЗ
записывается число 15 (строка 31). Выполнение цикла обеспечивают
оператор dec (строка 32) и оператор сравнения (строка 33). При
каждом проходе цикла 1оорЗ выполняются вложенные циклы loopl
и 1оор2. В результате общее количество проходов строенного цикла
возрастает еще в 15 раз, что обеспечивает требуемую задержку.
Кроме построенного цикла, подпрограмма задержки содержит уже
знакомые нам операторы сохранения и восстановления используемых
регистров. В нашем случае подпрограмма использует три регистра.
Поэтому В начале подпрограммы содержимое всех трех регистров
сохраняется в стеке (строки 28,29,30).
Перед выходом из подпрограммы содержимое всех этих регистров
восстанавливается (строки 41, 42, 43). Обратите внимание, что вос-
становление регистров происходит в порядке, обратном порядку
их запоминания. Регистр, ^соторый был записан в стек последним,
извлекается первым.
Программа на языке СИ
Как ВЫ уже наверно догадываетесь, осуществить задержку в програм-
ме, написанной на языке СИ, будет гораздо проще, чем на Ассемблере.
Листинг 1.8 содержит один из вариантов подобной программы.
Программа не содержит новых для нас операторов, поэтому сразу
перейдем к ее описанию. Для создания задержки используется та же
самая библиотека подпрограмм, что и в предыдущем примере (лис-
тинг 1.6). Однако, В нашем случае, мы берем другую функцию из этой
библиотеки. Функцию delay_ms (задержка в миллисекундах).
Рассмотрим подробнее работу программы. Все команды инициали-
зации в новой программе взяты из предьщущего примера и перене-
сены оттуда без изменений. Различия начинаются в главном цикле
программы.
74


Оператор i f в строке 10 производит проверку младшего разряда ре-
гистра PD на равенство единице. Если разряд равен единице (кнопка
не нажата), то выполняется строка 11 (запись в РВО единицы). Эта
строка выполняется все время, пока кнопка не нажата. В этом слу-
чае светодиод остается потушенным. Если нажать кнопку, младший
разряд PD окажется равным нулю. В этом случае вместо строки 11
выполняются строки 13—16.
Они представляют собой процедуру мигания светодиода. Эта проце-
дура работает следующим образом. В строке 13 светодиод тушится.
Затем осуществляется задержка на 200 мс (срока 14). В строке 15
светодиод зажигается. После этого опять осуществляется задержка
(строка 16). То есть выполняется один цикл мигания.
Так как вся конструкция if—else находится внутри основного цикла,
после окончания цикла мигания все операции повторяются сначала.
То есть снова выполняется проверка состояния кнопки (строка 10),
а по результатам проверки — одно их вышеописанных действий. В
случае, если кнопка все еще нажата, цикл мигания повторяется. Если
кнопка отпущена — просто гасится светодиод.
75


1.6. Бегущие огни
Постановка задачи
В прежние времена очень популярны среди радиолюбителей были
различные автоматы световых эффектов. Сейчас этим не удивить, и
совсем недорого можно купить готовую мигающую световую гирлян-
ду. Однако, как пример для программирования, такая задача вполне
подойдет. Итак, разрабатываем «Бегущие огни».
Задание будет звучать следующим образом:
«Разработать автомат «Бегущие огни» для управления составной
гирляндой из восьми отдельных гирлянд. Устройство должно обеспе-
чивать «движения» огня в двух разных направлениях. Переключение
направления «движения» должно осуществляться при помощи пере-
ключателя».
Схема
В соответствии с поставленной задачей наше устройство должно
управлять восемью световыми гирляндами. Удобно задействовать для
этого все восемь выходов одного из портов. Кроме того, нам придется
подключать переключатель направления. Для этого нам понадобится
еще один порт. Очевидно, что для такой задачи вполне подойдет уже
знакомый нам микроконтроллер ATtiny2313.
Для создания и отладки программы совсем не обязательно подклю-
чать К микроконтроллеру гирлянды лампочек. Для начала подключим
просто восемь светодиодов. Для подключения настоящей гирлянды
каждый светодиод нужно заменить ключевой схемой на тиристоре,
К которой уже подключить гирлянду. Примеры ключевых схем лег-
ко найти В радиолюбительской литературе. Схема бегущих огней со
светодйодами приведена на рис. 1.11.
Как видно из рис. 1.11, схема представляет собой доработанный ва-
риант схемы управления светодиодом (см. рис. 1.2). К предыдущей
схеме просто добавлены еще семь дополнительных светодиодов,
включенных таким же образом, как и светодиод VD1.
76


Алгоритм
Для создания эффекта «бегущих огней» удобнее всего воспользовать-
ся операторами сдвига, которые имеются в системе команд любого
микроконтроллера. Такие операторы сдвигают содержимое одного
из регистров микроконтроллера на один разряд влево или вправо.
Если сдвигать содержимое регистра и после каждого сдвига выводить
новое содержимое в порт РВ, подключенные к нему светодиоды будут
загораться поочередно, имитируя бегущий огонь. Алгоритм работы
бегущих огней может быть разный. Один из возможных алгоритмов
В самых общих чертах будет выглядеть следующим образом:
1. Считать состояние переключателя управления;
2. Если контакты переключателя разомкнуты, перейти к проце-
дуре сдвига вправо;
3. Если контакты замкнуты, перейти к процедуре сдвига влево;
4. После окончания полного цикла сдвига (восемь последователь-
ных сдвигов) перейти к началу алгоритма, то есть к пункту 1.
Таким образом, все время, пока контакты переключателя разомкнуты,
программа будет выполнять сдвиг вправо. Если состояние переклю-
чателя не изменилось, сдвиг в прежнем направлении продолжается.
Если замкнуть контакты переключателя, то все время, пока они
замкнуты, будет выполняться сдвиг влево. Как при сдвиге вправо,
так и при сдвиге влево после каждого полного цикла сдвига (8 шагов)
77


происходит проверка переключателя. Если его состояние не такое
же, как и прежде, то направление сдвига не изменяется. В противном
случае программа меняет направление сдвига.
Выполнение алгоритма сдвига
Посмотрим теперь, как выполняется сам алгоритм сдвига. Сдвиг
влево и сдвиг вправо выполняются аналогично. Ниже приводится
обобщенный алгоритм для сдвига влево и сдвига вправо, снабженный
комментариями.
1. Записать в рабочий регистр начальное значение. В качестве
начального значения используется двоичное число, у которого
один из разрядов равен единице, а остальные разряды равны
нулю. Для сдвига вправо нам нужно число с единицей в самом
старшем разряде (ОЫООООООО). Для сдвига влево в единицу
устанавливается младший разряд (ObOOOOOOOl).
2. Вывести значение рабочего регистра в порт РВ.
3. Вызвать подпрограмму задержки. Задержка нужна для того,
чтобы скорость «бега» огней была нормальная для глаз наблю-
дателя. Если бы не было задержки, то скорость «бега» была бы
столь велика, что мы бы и не увидели движения огней. С точ-
ки зрения наблюдателя мерцание огней выглядело бы как
слабое свечение всех светодиодов.
4. Сдвинуть содержимое рабочего регистра вправо (влево) на
один разряд.
5. Проверить, не окончился ли полный цикл сдвига (8 шагов).
6. Если полный цикл сдвига не закончен, перейти к пункту 2 данно-
го алгоритма. Это приведет к тому, что пункты 2,3,4,5 и 6 повто-
рятся 8 раз, и лишь затем завершится полный цикл сдвига.
Программа на Ассемблере
Возможный вариант программы приведен ниже (см. листинг 1.9). В
программе встречается несколько новых операторов. Кроме того, мы
будем иметь дело с новым для нас флагом. Этот флаг также является
одним из разрядов регистра SREG и называется флагом переноса.
78


Определение. Флаг переноса — это разряд, куда помещается бит
переноса при выполнении операций сложения двух чисел
или бит заема при операциях вычитания.
Содержимое флага переноса так же, как и содержимое флага нулевого
результата Z, может служить условием для оператора условного пере-
хода. Кроме своего основного предназначения, флаг переноса иногда
выполняет и вспомогательные функции. Например, он участвует во
всех операциях сдвига в качестве дополнительного разряда. Теперь
рассмотрим подробнее все новые операторы.
Isr
Логический сдвиг вправо. Этот оператор имеет всего один параметр —
имя регистра, содержимое которого сдвигается. Схематически данная
операция выглядит следующим образом:
О ^ d7 -> d6 -> d5 -> d4 -> d3 -> d2 -> dl -> dO ^ с.
To есть содержимое младшего разряда переносится в флаг переноса
с, на его место поступает содержимое разряда 1, в разряд 1 попадает
содержимое разряда 2, и так далее. В самый старший разряд записы-
вается ноль.
Isl
Логический сдвиг влево. Действие этого оператора обратно действию
предыдущего. Схема такого сдвига выглядит следующим образом:
C«-d7«-d6«-d5«-d4«-d3«-d2 >». Результатом выражения rab » 1 является число,
полученное путем сдвига всех разрядов переменной rab на одну
позицию вправо. Число 1 справа от оператора сдвига означает коли-
чество разрядов, на которое нужно сдвинуть число. Таким образом,
выражение
rab = rab » 1;
означает: присвоить переменной rab значение этой же переменной
сдвинутое на один разряд вправо. Язык СИ допускает другую, сокра-
щенную форму записи того же самого выражения:
rab »= 1;
Новое выражение полностью эквивалентно предьщущему. Подобные
изящные сокращения являются фирменной особенностью языка
си. Благодаря ним программа на языке СИ получается короче и
проще. В строке 16 вызывается функция задержки. Время задержки
составляет 200 мс.
Для того, чтобы ЦИКЛ сдвига повторялся только восемь раз, исполь-
зуется оператор цикла (строка 13). В качестве условия, при котором
цикл выполняется, используется выражение rab ! =0. В языке СИ
выражение «!=» означает «Не равно».
Таким образом, наш цикл сдвига (строки 14—16) будет выполняться
до тех пор, пока rab не равен нулю. Это и будут наши восемь шагов
86


сдвига. Чтобы убедиться в этом, еще раз посмотрите на табл. 1.2.
Значение одного из разрядов ЬО—Ь7, а, значит, и всей переменной
rab, во время первых восьми шагов не равно нулю. И только на де-
вятом шаге все восемь рабочих разрядов обнулятся. Но так как при
этом заложенное нами условие не выполняется, последнего девятого
цикла не будет.
Процедура сдвига влево находится в строках 18—22 программы и
работает точно так же, как процедура сдвига вправо. Имеются лишь
два отличия:
? другое начальное значение переменной rab (см. строку 18);
? применен другой оператор сдвига.
Для сдвига влево применяется оператор ««» (см. строку 21). При
желании выражение в строке 21 тоже можно сократить. Вместо
rab = rab ппжжжжп ? ? ^
листинг i.i^


1.8. Использование прерываний по таймеру
Постановка задачи
В предыдущем примере мы использовали таймер для формирования
задержки, но не использовали его главного преимущества: спосрб-
ности вызывать прерывания. На практике подобным образом почти
никогда не поступают. Чаще всего в подобных случаях применяют
прерывания по таймеру. Это позволяет более точно формировать
интервалы времени, но главное — позволяет разгрузить центральный
процессор.
Пока таймер формирует задержку, программа может выполнять
любые другие действия. В результате программу бегущих огней
можно легко совместить, например, с программой генерации звуков.
Но не будем усложнять нашу задачу и сформулируем ее следующим
образом:
«Создать новую программу «бегущих огней» с использованием преры-
ваний по таймеру».
Схема
Схему оставим без изменений (см. рис. 1.11).
Алгоритм
Поставленная выше задача потребует полной переделки всей на-
шей программы. Ведь изменится режим работы таймера. В данном
конкретном случае удобнее всего использовать режим совпадения.
Точнее, его подрежим «сброс при совпадении». В этом режиме таймер
сам периодически вырабатывает запросы на прерывание с заранее
заданным периодом.
Все функции управления «движением огней» выполняет процедура
обработки прерывания. При каждом вызове прерывания процедура
производит сдвиг «огней» на один шаг в нужном направлении.
Для того, чтобы обеспечить такую же скорость движения «огней»,
как В предыдущем примере, мы должны использовать те же самые
коэффициенты деления. Для начала необходимо включить предвари-
тельный делитель и выбрать для него коэффициент деления 1/1024.
98


Второй коэффициент деления (780) мы помещаем в специальный
системный регистр — регистр совпадения. Сравнение содержимого
счетного регистра с содержимым регистра совпадения будет происхо-
дить на аппаратном уровне. В режиме «сброс при совпадении» таймер
работает следующим образом. Сразу после запуска значение счетного
регистра начнет увеличиваться. Когда это значение окажется равным
значению регистра совпадения, таймер автоматически сбросится и
продолжит работу с нуля. В момент сброса таймера формируется
запрос на прерывание.
Для имитации бегущих огней, как и в предьщущих примерах, мы будем
использовать операции сдвига. При этом нам также понадобится спе-
циальный рабочий регистр. То есть один из регистров общего назначе-
ния, В котором будет храниться текущее состояние наших «огней». В
начале программы в рабочий регистр необходимо записать исходное
значение. То есть число, один из разрядов которого равен единице, а
остальные — нулю. В результате операций сдвига эта единица будет
перемещаться вправо или влево, создавая эффект бегущего огня.
Проверка состояния кнопки и сдвиг на один шаг будет производиться
при каждом вызове процедуры обработки прерывания.
Исходя из вышесказанного, алгоритм работы программы состоит из
двух независимых алгоритмов. Во-первых, это алгоритм основной
программы, а во-вторых, алгоритм процедуры обработки прерывания.
Рассмотрим их по порядку.
Алгоритм основной программы:
1. Настроить стек и порты ввода—вывода микроконтроллера;
2. Настроить таймер и систему прерываний;
3. Записать в рабочий регистр исходное значение;
4. Разрешить работу таймера;
5. Разрешить прерывания;
6. Перейти К выполнению основного цикла.
Так как все операции, связанные с движением огней, выполняет
процедура обработки прерываний, в основном цикле программы
нам ничего делать не нужно. Для простоты оставим основной цикл
пустым.
Алгоритм процедуры обработки прерывания:
1. Проверить состояние переключателя режимов;
99


2. Если контакты переключателя разомкнуты, произвести сдвиг
всех разрядов рабочего регистра на один разряд вправо. Если в
результате этого сдвига единичный бит выйдет за пределы бай-
та, создать новый единичный бит в крайней левой позиции;
3. Если контакты переключателя замкнуты, произвести сдвиг всех
разрядов рабочего регистра на один разряд влево. Если в ре-
зультате этого сдвига единичный бит выйдет за пределы байта,
создать новый единичный бит в крайней правой позиции;
4. Вывести содержимое рабочего регистра в порт РВ, предвари-
тельно проинвертировав его;
5. Закончить процедуру обработки прерывания.
Программа на Ассемблере
Текст возможного варианта программы на языке Ассемблер приведен
В листинге 1.13. В программе встречаются несколько новых для нас
операторов.
Обратите внимание. Используется новый для нас флаг — флаг гло-
бального разрешения прерываний, который
называется I.
Мы уже упоминали этот флаг в главе 3. Флаг I, так же, как флаги С и
Z, является одним из разрядов регистра SREG. Однако управление
флагом I происходит совсем по-другому. На него не влияют ни ариф-
метические, ни логические операции, а тем более операции сравнения.
Для установки и сброса этого флага в системе команд предусмотрены
две специальные команды (описаны ниже). Если флаг I сброшен, то
все прерывания в микроконтроллере запрещены. Если флаг установ-
лен, работа системы прерываний разрешается. Рассмотрим теперь по
порядку все новые для нас операторы.
.dseg
Оператор выбора сегмента памяти данных. До сих пор во всех пре-
дыдущих ассемблерных программах мы обязательно использовали
оператор . cseg, который позволял нам выбирать программный
сегмент памяти. Пора научиться работать и с другими сегментами.
100


Следующий по значению после программного сегмента — это сегмент
памяти данных, то есть сегмент ОЗУ. В программе (листинг 1.13) в
строке 6 производится выбор именно этого сегмента.
.byte
Оператор резервирования памяти. Это один из операторов, которые
действуют В сегменте памяти данных. Оператор позволяет зарезер-
вировать один или несколько байтов (ячеек ОЗУ) для того, чтобы
затем использовать их в программе. Вы спросите: зачем это нужно?
Основная цель резервирования — учет и распределение памяти.
Если профаммист будет произвольно, по своему усмотрению, выбирать
адреса ячеек ОЗУ для той либо иной задачи, то ему придется внима-
тельно следить за тем, чтобы не выбрать повторно одну и ту же ячейку
для хранения разных значений. Иначе программа при записи одного
значения испортит второе, что приведет к ошибке в ее работе.
Механизм резервирования памяти позволяет транслятору контроли-
ровать использование памяти и исключать двойное использование
ячеек. Кроме того, подобный механизм вообще избавляет програм-
миста ОТ необходимости запоминать адреса. Все происходит автома-
тически.
Оператор . byte имеет всего один параметр — количество ячеек,
которые нужно зарезервировать. В нашей программе применяется
лишь одна команда, резервирующая память (строка 8, листинг 1.13).
В данном случае резервируется всего одна ячейка памяти. Метка
buf, поставленная перед оператором, используется для обращения
К зарезервированной ячейке.
red
Оператор завершения подпрограммы обработки прерывания. Действие
этого оператора аналогично действию оператора ret. Он извлекает
адрес из стека и передает управление по этому адресу. Различие со-
стоит В ТОМ, ЧТО команда reti еще и устанавливает в единицу флаг
глобального разрешения прерываний I.
sts
Команда записи содержимого РОН в ОЗУ. Имеет два параметра.
Первый параметр — адрес ячейки памяти, куда записываются данные.
101


Программа на языке СИ
Как мы убедились на примере Ассемблера, для нашей новой задачи
потребуются довольно значительные изменения программы. При раз-
работке программы средствами системы CodeVisionAVR в подобной
ситуации целесообразнее воспользоваться мастером, при помощи
которого удобно создать новую заготовку программы.
Для создания новой заготовки программы удобно восстановить
настройки из созданного ранее примера, подкорректировать их в
соответствии с новыми требованиями и записать под новым именем.
Для этого зайдем в программу Code VisionAVR и запустим мастер.
Для запуска мастера достаточно нажать на панели инструментов
кнопку ^.
После запуска мастера все его управляющие элементы будут нахо-
диться В исходном состоянии. То есть иметь значения по умолчанию.
Теперь нам нужно восстановить настройки из самого первого примера
(см. раздел 1.2).
Для этого В меню «File» мастера выберем пункт «Open», как показано
на рис. 1.12. Появится стандартное окно открытия файла. Найдите на
вашем диске файл Progl.cwp и откройте его. После того, как вы его
откроете, все органы управления во всех вкладках мастера примут
те значения, какие они имели при создании программы Progl (см.
рис. 1.3—1.6).
Теперь нужно сохранить этот пример с другим именем. Для этого
при помощи пункта меню «File / Save As» сделайте копию только что
загруженного файла. Копию поместите в новую директорию с именем
Prog7. В этой директории будет храниться наш новый проект. Теперь
можно приступать к изменениям настроек под новые требования.
Исходя из технического задания, нам придется изменить лишь
настройки нашего таймера. Для этого сначала откройте вкладку
«Timers». На этой вкладке вы увидите еще три вкладки поменьше
(см. рис. 1.13). Эти вкладки предназначены для настройки двух
системных таймеров (TimerO и Timer 1), а также охранного таймера
(Watchdog). Открываем вкладку «Timerl», как показано на рис. 1.13.
При помощи расположенных там элементов управления выставляем
следующие настройки:
? поле «Clock Source» (Источник Тактового Сигнала) оставляем
В положении «System Clock» (Тактовый сигнал от системного
генератора).
? В поле «Clock Value» (Значение тактовой частоты) выбираем
значение 3,906 кГц. На самом деле мы выбираем коэффициент
110
 
Сайт управляется системой uCoz